C Programming

A D-Flat++ program starts with an application window on a desktop. Al presents the source files that contain the member functions for the desktop and its devices.


December 01, 1992
URL:http://www.drdobbs.com/cpp/c-programming/184408902

DEC92: C PROGRAMMING

I am writing this in September, having just returned from C++ in Action, the Santa Clara, California edition of the conference whose name keeps changing. The one I attended six months ago in New Jersey was C/C++ in Action. They've dropped the C part. Seems like nobody wants to talk about C anymore. That's OK, it's all been said. Before that it was C/C++ at Work. I gave a talk on C++ persistent objects, the essence of which you will find in my article, "Persistent Objects in C++," elsewhere in this issue. The conference and exposition were well attended, which is well deserved. Interest in C++ continues to heat up, and C++ in Action, under the steerage of technical director Chris Skelly, covers the subject comprehensively and with many relevant and fascinating sessions. I was not there long enough to attend as many of them as I wanted, and they always manage to schedule the ones that interest me most at the same time, but I did make it to P.J. (Bill) Plauger's "C++ for Pragmatists" keynote address, which he whimsically subtitled "Making the Move from C++ to C." Bill always manages to speak straight to the heart of a programmer's concerns, at the same time delighting us by bumping off a graven image or two.

D-Flat++ on the Desktop

Last month we began the D-Flat++ project by showing the header files that describe the desktop and hardware device classes. A DF++ application program starts with an application window on a desktop. The application instantiates the application window and associates a menu and menu command member functions with the application window. The application window connects itself to the global desktop, which contains the device objects for the user interface. This month we will look at the .CPP source files that contain the member functions for the desktop and its devices.

Listing One, page 154, is desktop.cpp, which contains the code for the desktop itself. It begins by establishing the exception-handling logic for out-of-memory conditions. Although contemporary C++ literature defines exception handling, no PC compiler implements it yet. The set_new_handler function has been in C++ for a while, however, and it provides a place for programs to go when the system runs out of memory. Borland C++ implements set_new_handler according to C++ tradition, but Microsoft C++ uses its own nonconventional format, so desktop.cpp starts out with a compile-time conditional to comply with whichever compiler you are using. As of this writing, I am developing with the Borland compiler and then porting the code to the Microsoft compiler, which is usually a seamless port.

Each DF++ application starts with a default Desktop object named, appropriately, "desktop." When the application declares an application window, the application-window constructor associates itself with the global desktop object. The Desktop class definition includes embedded objects for the devices on the desktop. The Desktop class constructor sets the default window pointers to null and establishes the address of the out-of-memory exception handler, which exits from the program. Then the constructor tells the cursor object to hide itself. Since the desktop object is global, its constructor runs ahead of the main function. The destructor runs when main returns and tells the cursor object to show itself again, the assumption being that the program runs from the command line where the cursor is needed and that individual document windows will show the cursor if they need it.

The Desktop::DispatchEvents method calls the DispatchEvents method for each device object that can generate event messages. The keyboard, mouse, and clock can generate such events, so each has a function to dispatch its events to whichever window should receive them.

Listings Two through Seven are screen.cpp, mouse.cpp, cursor.cpp, keyboard.cpp, speaker.cpp, and clock.cpp. These modules contain the member functions for the device classes defined in the header files from last month.

The screen object. The screen.cpp file (Listing Two, page 154) manages output to the screen as well as reading and writing video memory. The Screen constructor determines the display type and video mode, page, and memory address. It reaches into the PC's BIOS RAM Data Area to get the current character height and width of the screen. This code, like that of the other modules this month, is heavily dependent on the system hardware. The rest of DF++ is as independent of hardware as possible. There is a lot of low-level code in screen.cpp. The module directly accesses video memory and uses the INT86 function to make video BIOS calls.

The class includes member functions to test for an EGA or VGA monitor, which will determine the screen formats that you may select. There is a function to scroll a portion of the screen and functions to read and write individual characters of video memory based on screen coordinates. There is also a function to write a string to video memory, which uses a block move rather than a series of character moves. This is a performance strategy. Whenever it can, DF++ writes to the screen one line at a time.

The last two functions get and put video memory in large blocks. The blocks are described by Rect objects passed as parameters. The buffers are assumed to be holders for video memory that can fit into the rectangles.

The mouse object. The mouse.cpp file (Listing Three, page 154) contains the code to manage the Mouse object. The constructor determines if a mouse device driver is installed by looking at the mouse's interrupt vector. If the vector is 0 or if it points to an iret machine-language operation code, the mouse driver is not running, and the program will not attempt to use the mouse.

The mouse constructor and destructor save and restore the mouse state. I'm not sure how necessary that is unless you use DF++ to develop a TSR. Why anyone would use C++ to write TSRs is beyond me, though. That'd be like driving to work in the Concorde. Or George Bush campaigning from the back of a train. (Uh-oh, I spoke too soon. There he is on TV, hanging off the back of his private railroad car hollering at a crowd of bewildered country folk and trying to look like HST. No way, Mr. President.)

The other functions get and set the mouse cursor position, show and hide the mouse cursor, determine if the mouse has moved since the last time the program checked, see if the button has been pressed or released, and describe the mouse's boundaries of travel on the screen. The DispatchEvent function, called by the Desktop object's function of the same name, calls functions to dispatch events related to button presses and releases and mouse movements.

The MouseWindow function determines which window should receive the mouse event. If a window has captured the focus, the event goes to that window regardless of the mouse cursor's position. Otherwise, the event goes to whichever window the mouse cursor is in. The global in Window function, in another source file, returns a window pointer given a set of coordinates. It determines the window by observing its position relative to other windows that share the same coordinates but that are overlaid. The foremost window is the one returned.

The DispatchLeftButton function controls the typematic-like behavior of the mouse. If the user holds the mouse button down for a while, the program sends repeated left-button events to the window. The DispatchRelease function not only dispatches a release event, but sets a timer. If a second release event occurs before the timer runs down, the function dispatches a double-click event. The DispatchMove function sends a move event if the mouse cursor is in a position other than where it was the last time the program looked at it.

The cursor object. Cursor.cpp (Listing Four, page 155) contains the code to show, hide, move, read, and size the screen's keyboard cursor. These are all standard BIOS operations except for saving and restoring the cursor. Its shape and state of visibility are on a stack. Windows that use the cursor save the cursor before they do anything with it, and they restore it when they are done. Since this process can occur in nested windows, the stack assures that the cursor is restored to its correct configuration as different windows open and close, acquiring and releasing the focus.

The keyboard object. Keyboard.cpp (Listing Five, page 156) contains the member functions for the Keyboard object. Its DispatchEvents function determines which window should get a keyboard event by using the following rules of precedence. If a window has captured the focus, that window gets the event. Otherwise, the event goes to the window that has the focus, if any. If no window has the focus, the event goes to the application window.

The Keyboard object sends two events, one when the user presses a key and one when the shift status changes. The class also provides the AltConvert function to convert Alt+ keys to their ASCII letter or number equivalent.

The speaker object. Speaker.cpp (Listing Six, page 156) contains the Beep function to make a sound through the computer's audio system. The function drives the system's speaker by programming a frequency, starting the sound, and leaving it on for two ticks of the system clock. The resulting buzz is used by DF++ to tell the user that a selection is not valid.

The clock object. Clock.cpp (Listing Seven, page 156) sends the clock-tick event to the application window every 19 ticks of the system's interval timer. This approximates one event per second, but it is not precise because timer ticks occur 18.2 times per second. The purpose of the event is to allow the application window's status bar to display the time of day without needing to poll the system's time-of-day clock unnecessarily.

How to Get the Source Code

D-Flat++ is available in its first incarnation from the DDJ Forum libraries on CompuServe or directly from me. Send a stamped, self-addressed diskette mailer and a formatted 360K or 720K diskette to me at D-Flat++, Dr. Dobb's Journal, 411 Borel Ave., San Mateo, CA, 94402-3522. Specify that you want D-Flat++. If you want D-Flat as well, specify that, too. The software is free, but if you wish, you can participate in our Careware program by including a dollar, which I will donate to the Brevard County Food Bank.

The first version of DF++ is minimal at best, and should serve to acquaint you with the structure of the class library and how to use it. You could develop a small application with it, but there is more to come.

Book Report: Windows++

For a long time I've wanted to build a class library similar to D-Flat++ but for Microsoft Windows. That's not a new idea. There are already many such libraries: Borland's Object Window for Libraries (OWL) and Microsoft's Foundation Classes (MFC) are two prominent examples. Nonetheless, such a library, devoid of the bulk that usually accompanies a commercial product that covers all bases would make for an interesting project to develop and write about. I've put it off because I've had no Windows programs to write. I never really considered doing the library from this column because I do not want to bind the column to the Windows platform for that long. It would make a good book, however. And so it has.

Windows++, by Paul DiLascia (Addison-Wesley, 1992), is such a book. This is the kind of programmer's book that I like. It teaches in an orderly progression of lessons that build upon one another, and it includes a large body of C++ source code that the reader can use to develop real programs. It's a perfect stocking stuffer for the programmers on your list.

The book contains a full class library that encapsulates enough of the Windows API to satisfy most applications. Where the library is incomplete, it tells you how to enhance the library to include what you need. Chapter 1 introduces the concept of the Windows++ class library, how the author came to develop it, and shows you what a "hello, world" program will look like after you learn how to use the library. Chapter 2 is about C++, but you really need a better introduction if you do not already know the language. It's difficult to find a reason for this chapter. If you already know C++, you do not need it. If you do not know C++, you need more than what you get.

Chapter 3 regresses by writing the same "hello, world" program in C using the Windows API and the SDK. The author assumes that you understand Windows programming and that you will understand that program. He wants you to contrast the complexity of the C version with that of the simpler C++ version from Chapter 1. Then, step by step, he rewrites the C program, moving a piece of it at a time into the encapsulated C++ class library. In the final step, you are back to the original C++ program, which shows you that much of the complexity of the Windows API is routine, and that the complexity can be hidden in the encapsulated classes.

Chapter 4 is a gem. It describes the ridiculous state of Windows memory management and the absurd hoops that programmers jump through to use different memory models, and then it proceeds to solve the problem. You not only learn how to solve it, you get the solution encapsulated in the class library so you can forget about it.

Chapter 5 presents a couple of example programs, each one revealing new things about Windows programming and about the Windows++ class library. Chapter 6 shows you how to manage mundane tasks such as disabling menu commands. You learn to encapsulate routine user-interface procedures such as the ubiquitous File Open dialog box and the Edit menu commands. Windows++ includes encapsulation of multiple document-interface windows as well, and Chapter 6 covers that, too.

Dialog boxes get a full chapter of their own. You learn not only how to include dialog boxes in your Windows++ programs but how the classes work to implement the dialog boxes and their controls. The subject of graphics has its own chapter, too. Early in the book, the author claims that Windows++ is superior to Borland's OWL. Graphics is an example of that. Windows++ includes classes to support the Windows graphics device interface (GDI), and OWL does not.

A Windows++ bonus is the chapter on creating a dynamic link library (DLL) for the class library. Besides explaining how DLLs themselves work and then putting the Windows++ class library into a DLL, the chapter is a guide to building DLLs for other class libraries as well, which is an important feature for developers of programs that run in a multitasking environment.

I compiled all the Windows++ example programs and ran them. The Mandelbrot program kills my system, making Windows behave erratically, but the author could not reproduce the problem on his computer and had not heard of that problem from anyone else. Everything else worked without a hitch. The book delivers what it promises. It is unique. No other work that I have seen comes close to what Windows++ brings to the C++ Windows programmer. Even if you use a different class library or application framework for Windows development, this book is invaluable for showing you how it all fits together.

The book does not claim to cover everything there is to know about Windows programming, and the class library does not encapsulate all the features available to a Windows programmer. There is no mention of using the communications ports in a Windows program, a sure candidate for the kind of encapsulation that Windows++ provides. The book ignores dynamic data exchange (DDE) and object linking and embedding (OLE), two prominent Windows features. Although the class library supports clipboard operations, the book gives one brief paragraph to the subject, referring the reader to the source code for details.

My only serious criticism is of a characteristic that I've been seeing a lot of in current computer books. Although I like the breezy, informal style that runs throughout the book, sometimes it gets too cute and the author too chummy. Mind you, columnists can get away with it because you know us, but in a technical book or article those occasional familiarities deflect our attention from the subject, and for the moment we have to deal with the author rather than with what he or she is teaching. As in all social circumstances where people interact, the stranger must gain acceptance before revealing the more personal sides of his or her personality. After talking to Paul for a while, his book was easier to read because I knew his voice. Before that, however, his unnecessary use of slang and clever asides seemed strained, and it detracted from what is an otherwise excellent and relevant book.

Coming Attractions

In the coming months we'll look at how the DF++ application window works, how to build menus and dialog boxes, and an example application or two. I've developed a program for making textmode screen snapshots on a LaserJet, and I'll be showing you that as well.



_C PROGRAMMING COLUMN_
by Al Stevens


[LISTING ONE]


// --------------- desktop.cpp

#include <new.h>
#include "desktop.h"

DeskTop desktop;

#ifdef MSC
int NoMemory(unsigned int)
{
    exit(-1);
    return 0;
}
#else
void NoMemory()
{
    exit(-1);
}
#endif
DeskTop::DeskTop()
{
    apwnd = infocus = firstcapture = focuscapture = NULL;
#ifdef MSC
    _set_new_handler(&NoMemory);
#else
    set_new_handler(NoMemory);
#endif
    syscursor.Hide();
}
DeskTop::~DeskTop()
{
    syscursor.Show();
}
Bool DeskTop::DispatchEvents()
{
    syskeyboard.DispatchEvent();
    sysmouse.DispatchEvent();
    sysclock.DispatchEvent();
    return (Bool) (apwnd != NULL);
}








[LISTING TWO]


// ----------- screen.cpp

#include <string.h>
#include "desktop.h"

Screen::Screen()
{
    if (isEGA() || isVGA())    {
        // --- turn blinking off
        regs.x.ax = 0x1003;
        regs.h.bl = 0;
        int86(VIDEO, ®s, ®s);
    }
    // ---- get the video mode and page
    regs.h.ah = 15;
    int86(VIDEO, ®s, ®s);
    mode = regs.h.al;
    page = regs.x.bx;
    page &= 0xff00;
    mode &= 0x7f;
    // ---- Monochrome Display Adaptor or text mode
    if (isMono())
        address = 0xb000;
    else
        // ------ Text mode
        address = 0xb800 + page;
    width = *(unsigned char far *)( MK_FP(0x40,0x4a) );
    if (isVGA() || isEGA())
        height = *(unsigned char far *)( MK_FP(0x40,0x84) )+1;
    else
        height = 25;
}
// ---- test for EGA
Bool Screen::isEGA(void)
{
    if (isVGA())
        return False;
    regs.h.ah = 0x12;
    regs.h.bl = 0x10;
    int86(VIDEO, ®s, ®s);
    return (Bool) (regs.h.bl != 0x10);
}
// ---- test for VGA
Bool Screen::isVGA(void)
{
    regs.x.ax = 0x1a00;
    int86(VIDEO, ®s, ®s);
    return (Bool) (regs.h.al == 0x1a && regs.h.bl > 6);
}
// --------- scroll the screen d: 1 = up, 0 = dn
void Screen::Scroll(Rect &rc, int d, int fg, int bg)
{
    desktop.mouse().Hide();
    regs.h.cl = rc.Left();
    regs.h.ch = rc.Top();
    regs.h.dl = rc.Right();
    regs.h.dh = rc.Bottom();
    regs.h.bh = clr(fg,bg);
    regs.h.ah = 7 - d;
    regs.h.al = 1;
    int86(VIDEO, ®s, ®s);
    desktop.mouse().Show();
}
// -------- read a character of video memory
unsigned int Screen::GetVideoChar(int x, int y)
{
    int c;
    desktop.mouse().Hide();
    c = peek(address, vad(x,y));
    desktop.mouse().Show();
    return c & 255;
}
// -------- write a character of video memory
void Screen::PutVideoChar(int x, int y, unsigned int c)
{
    if (x < width && y < height)    {
        desktop.mouse().Hide();
        poke(address, vad(x,y), c);
        desktop.mouse().Show();
    }
}
// --------- Write a string to video memory
void Screen::WriteVideoString(char *s,int x,int y,int fg,int bg)
{
    if (x < width && y < height)    {
        int len = strlen(s);
        int *ln = new int[len];
        int *cp1 = ln;
        int col = clr(fg,bg) << 8;
        while (*s)    {
            *cp1++ = (*s & 255) | col;
            s++;
        }
        if (x + len >= width)
            len = width - x;
        desktop.mouse().Hide();
        movedata(FP_SEG(ln),FP_OFF(ln),address,vad(x,y),len*2);
        desktop.mouse().Show();
        delete [] ln;
    }
}
// -- read a rectangle of video memory into a save buffer
void Screen::GetBuffer(Rect &rc, char *bf)
{
    if (rc.Left() >= width || rc.Top() >= height)
        return;
    int ht = rc.Bottom()-rc.Top()+1;
    int bytes_row = (rc.Right()-rc.Left()+1) * 2;
    unsigned vadr = vad(rc.Left(), rc.Top());
    desktop.mouse().Hide();
    while (ht--)    {
        movedata(address, vadr, FP_SEG(bf),
                FP_OFF(bf), bytes_row);
        vadr += width*2;
        bf = (char far *)bf + bytes_row;
    }
    desktop.mouse().Show();
}
// -- write a rectangle of video memory from a save buffer
void Screen::PutBuffer(Rect &rc, char *bf)
{
    if (rc.Left() >= width || rc.Top() >= height)
        return;
    int ht = rc.Bottom()-rc.Top()+1;
    int bytes_row = (rc.Right()-rc.Left()+1) * 2;
    unsigned vadr = vad(rc.Left(), rc.Top());
    desktop.mouse().Hide();
    while (ht--)    {
        movedata(FP_SEG(bf), FP_OFF(bf), address,
                vadr, bytes_row);
        vadr += width*2;
        bf += bytes_row;
    }
    desktop.mouse().Show();
}








[LISTING THREE]


// ------------- mouse.cpp

#include <stdio.h>
#include "desktop.h"

// -------- mouse constructor
Mouse::Mouse()
{
    // ------- see if mouse driver is installed
    unsigned char far *ms;
    ms = (unsigned char far *)
        MK_FP(peek(0, MOUSE*4+2), peek(0, MOUSE*4));
    // --- if the interrupt vector is null or points to a retf,
    //     the mouse driver is not installed
    installed = (Bool) (ms != NULL && *ms != 0xcf);

    if (installed)    {
        // --- get the mouse state buffer size
        CallMouse(BUFFSIZE);
        statebuffer = new char[regs.x.bx];
        // --- save the mouse state
        CallMouse(SAVESTATE, 0, 0,
            FP_OFF(statebuffer), FP_SEG(statebuffer));
        // --- reset the mouse
        CallMouse(RESETMOUSE);
        prevx = prevy =
        clickx = clicky =
        releasex = releasey -1;
        SetTravel(0, desktop.screen().Width()-1, 0,
                                                  desktop.screen().Height()-1);
    }
}
Mouse::~Mouse()
{
    if (installed)    {
        Hide();
        // --- restore the mouse state
        CallMouse(RESTORESTATE, 0, 0,
            FP_OFF(statebuffer), FP_SEG(statebuffer));
        delete [] statebuffer;
    }
}
void Mouse::GetPosition(int &mx, int &my)
{
    mx = my = 0;
    if (installed)    {
        CallMouse(READMOUSE);
        mx = regs.x.cx/8;
        my = regs.x.dx/8;
        if (desktop.screen().Width() == 40)
            mx /= 2;
    }
}
void Mouse::SetPosition(int x, int y)
{
    if (installed)    {
        if (desktop.screen().Width() == 40)
            x *= 2;
        CallMouse(SETPOSITION,0,x*8,y*8);
    }
}
Bool Mouse::Moved()
{
    int x, y;
    Bool rtn = False;
    if (installed)    {
        GetPosition(x, y);
        rtn = (Bool) (x != prevx || y != prevy);
        prevx = x;
        prevy = y;
    }
    return rtn;
}
void Mouse::Show()
{
    if (installed)
        CallMouse(SHOWMOUSE);
}
void Mouse::Hide()
{
    if (installed)
        CallMouse(HIDEMOUSE);
}
Bool Mouse::LeftButton()
{
    Bool rtn = False;
    if (installed)    {
        CallMouse(READMOUSE);
        rtn = (Bool) ((regs.x.bx & 1) == 1);
    }
    return rtn;
}
Bool Mouse::ButtonReleased()
{
    Bool rtn = False;
    if (installed)    {
        CallMouse(BUTTONRELEASED);
        rtn = (Bool) (regs.x.bx != 0);
    }
    return rtn;
}
void Mouse::SetTravel(int minx, int maxx, int miny, int maxy)
{
    if (installed)    {
        if (desktop.screen().Width() == 40)    {
            minx *= 2;
            maxx *= 2;
        }
        CallMouse(XLIMIT, 0, minx*8, maxx*8);
        CallMouse(YLIMIT, 0, miny*8, maxy*8);
    }
}
void Mouse::CallMouse(int m1,int m2,int m3,int m4, unsigned es)
{
    struct SREGS sregs;
    segread(&sregs);
    if (es != 0)
        sregs.es = es;
    regs.x.dx = m4;
    regs.x.cx = m3;
    regs.x.bx = m2;
    regs.x.ax = m1;
    int86x(MOUSE, ®s, ®s, &sregs);
}
// ------ get the window to send mouse events
DFWindow *Mouse::MouseWindow(int mx, int my)
{
    DFWindow *Mwnd;
    if (desktop.FocusCapture() != NULL)
        Mwnd = desktop.FocusCapture();
    else
        Mwnd = inWindow(mx, my);
    return Mwnd;
}
void Mouse::DispatchRelease()
{
    if (ButtonReleased())    {
        int mx, my;
        GetPosition(mx, my);
        DFWindow *Mwnd;
        if ((Mwnd = MouseWindow(mx, my)) == NULL)
            return;
        // ------- disable typematic check
        clickx = clicky = -1;
           delaytimer.DisableTimer();
        // ------- the button was released
        if (mx == releasex && my == releasey)    {
            // ---- same position as last left button release
            if (doubletimer.TimerRunning())    {
                // -- second click before double timeout
                doubletimer.DisableTimer();
                Mwnd->DoubleClick(mx, my);
                releasex = releasey = -1;
            }
        }
        else    {
            doubletimer.SetTimer(DOUBLETICKS);
            Mwnd->ButtonReleased(mx, my);
               releasex = mx;
               releasey = my;
        }
    }
}
void Mouse::DispatchMove()
{
    if (Moved())    {
        int mx, my;
        GetPosition(mx, my);
        DFWindow *Mwnd;
        if ((Mwnd = MouseWindow(mx, my)) != NULL)    {
            Mwnd->MouseMoved(mx, my);
            clickx = clicky = -1;
        }
    }
}
void Mouse::DispatchLeftButton()
{
    if (LeftButton())    {
        int mx, my;
        GetPosition(mx, my);
        DFWindow *Mwnd;
        if ((Mwnd = MouseWindow(mx, my)) == NULL)
            return;
        if (mx == clickx && my == clicky)    {
            if (delaytimer.TimedOut())    {
                // ---- button held down a while
                delaytimer.SetTimer(DELAYTICKS);
                // ---- post a typematic-like button
                Mwnd->LeftButton(mx, my);
            }
        }
        else    {
            // --------- new button press
            delaytimer.SetTimer(FIRSTDELAY);
            if (Mwnd->SetFocus())
                Mwnd->LeftButton(mx, my);
            clickx = mx;
               clicky = my;
        }
    }
}
// -------- dispatch mouse events
void Mouse::DispatchEvent()
{
    DispatchRelease();
    DispatchMove();
    DispatchLeftButton();
}








[LISTING FOUR]


// ------------ cursor.cpp

#include <dos.h>
#include "cursor.h"
#include "desktop.h"

Cursor::Cursor()
{
    cs = 0;
    Save();
}
Cursor::~Cursor()
{
    Restore();
}
// ------ get cursor shape and position
void Cursor::GetCursor()
{
    regs.h.ah = READCURSOR;
    regs.x.bx = desktop.screen().Page();
    int86(VIDEO, ®s, ®s);
}
// -------- get the current cursor position
void Cursor::GetPosition(int &x, int &y)
{
    GetCursor();
    x = regs.h.dl;
    y = regs.h.dh;
}
// ------ position the cursor
void Cursor::SetPosition(int x, int y)
{
    regs.x.dx = ((y << 8) & 0xff00) + x;
    regs.h.ah = SETCURSOR;
    regs.x.bx = desktop.screen().Page();
    int86(VIDEO, ®s, ®s);
}
// ------ save the current cursor configuration
void Cursor::Save()
{
    if (cs < MAXSAVES)    {
        GetCursor();
        cursorshape[cs] = regs.x.cx;
        cursorpos[cs] = regs.x.dx;
        cs++;
    }
}
// ---- restore the saved cursor configuration
void Cursor::Restore()
{
    if (cs)    {
        --cs;
        regs.x.dx = cursorpos[cs];
        regs.h.ah = SETCURSOR;
        regs.x.bx = desktop.screen().Page();
        int86(VIDEO, ®s, ®s);
        SetType(cursorshape[cs]);
    }
}
/* ---- set the cursor type ---- */
void Cursor::SetType(unsigned t)
{
    regs.h.ah = SETCURSORTYPE;
    regs.x.bx = desktop.screen().Page();
    regs.x.cx = t;
    int86(VIDEO, ®s, ®s);
}
/* ----- swap the cursor stack ------- */
void Cursor::SwapStack()
{
    if (cs > 1)    {
        swap(cursorpos[cs-2], cursorpos[cs-1]);
        swap(cursorshape[cs-2], cursorshape[cs-1]);
    }
}
/* ------ hide the cursor ------ */
void Cursor::Hide()
{
    GetCursor();
    regs.h.ch |= HIDECURSOR;
    regs.h.ah = SETCURSORTYPE;
    int86(VIDEO, ®s, ®s);
}
/* ------ show the cursor ------ */
void Cursor::Show()
{
    GetCursor();
    regs.h.ch &= ~HIDECURSOR;
    regs.h.ah = SETCURSORTYPE;
    int86(VIDEO, ®s, ®s);
}







[LISTING FIVE]


// ----------- keyboard.cpp

#include <stdio.h>
#include <bios.h>
#include <dos.h>
#include "desktop.h"

/* ----- table of alt keys for finding shortcut keys ----- */
static int altconvert[] = {
    ALT_A,ALT_B,ALT_C,ALT_D,ALT_E,ALT_F,ALT_G,ALT_H,
    ALT_I,ALT_J,ALT_K,ALT_L,ALT_M,ALT_N,ALT_O,ALT_P,
    ALT_Q,ALT_R,ALT_S,ALT_T,ALT_U,ALT_V,ALT_W,ALT_X,
    ALT_Y,ALT_Z,ALT_0,ALT_1,ALT_2,ALT_3,ALT_4,ALT_5,
    ALT_6,ALT_7,ALT_8,ALT_9
};
/* ---- Test for keystroke ---- */
#ifndef MSC
Bool Keyboard::KeyHit()
{
    _AH = 1;
    geninterrupt(KEYBRD);
    return (Bool)((_FLAGS & ZEROFLAG) == 0);
}
#else
Bool Keyboard::KeyHit()
{
    return (Bool) (bioskey(1) != 0);
}
#endif
/* ---- Read a keystroke ---- */
int Keyboard::GetKey()
{
    int c;
    while (KeyHit() == False)
        ;
    if (((c = bioskey(0)) & 0xff) == 0)
        c = (c >> 8) | 0x1080;
    else
        c &= 0xff;
    return c & 0x10ff;
}
/* ---------- read the keyboard shift status --------- */
int Keyboard::GetShift()
{
    regs.h.ah = 2;
    int86(KEYBRD, ®s, ®s);
    return regs.h.al;
}
/* ------ convert an Alt+ key to its letter equivalent ----- */
int Keyboard::AltConvert(int c)
{
    int i, a = 0;
    for (i = 0; i < 36; i++)
        if (c == altconvert[i])
            break;
    if (i < 26)
        a = 'a' + i;
    else if (i < 36)
        a = '0' + i - 26;
    return a;
}
Bool Keyboard::ShiftChanged()
{
    int sk = GetShift();
    Bool rtn = (Bool) (sk != shift);
    shift = sk;
    return rtn;
}
// ------ dispatch keyboard events
void Keyboard::DispatchEvent()
{
    // ---- find window for keyboard events
    DFWindow *Kwnd = desktop.FocusCapture() ?
                     desktop.FocusCapture() :
                     desktop.InFocus() ?
                     desktop.InFocus() : desktop.ApplWnd();
    if (ShiftChanged())
           // ---- the shift status changed
        Kwnd->ShiftChanged(GetShift());
    if (KeyHit())
        // --- a key was pressed
        Kwnd->Keyboard(GetKey());
}








[LISTING SIX]


// -------- speaker.cpp

#include <dos.h>
#include <conio.h>
#include "speaker.h"
#include "dflatdef.h"

// -------- sound a tone
void Speaker::Beep()
{
    outp(0x43, 0xb6);               // program the frequency
    outp(0x42, (int) (COUNT % 256));
    outp(0x42, (int) (COUNT / 256));
    outp(0x61, inp(0x61) | 3);      // start the sound
    // -------- wait two clock ticks
    const int far *clk = (int far *) MK_FP(0x40,0x6c);
    int then = *clk+2;
    while (*clk < then)
        ;
    outp(0x61, inp(0x61) & ~3);     // stop the sound
}






[LISTING SEVEN]


// ------- clock.cpp

#include "desktop.h"

Clock::Clock()
{
    clocktimer.SetTimer(0);
}
void Clock::DispatchEvent()
{
    if (clocktimer.TimedOut())    {
        // -------- reset the timer
        clocktimer.SetTimer(19);    // approx. 19 tics/second
        // -------- post the clock event
        if (desktop.ApplWnd() != NULL)
            desktop.ApplWnd()->ClockTick();
    }
}












Copyright © 1992, Dr. Dobb's Journal

Terms of Service | Privacy Statement | Copyright © 2024 UBM Tech, All rights reserved.