# HG changeset patch # User Eris Caffee # Date 1335075933 18000 # Node ID 8769ee69bd05f12501cca0e8e9b978196ada8760 # Parent c5c33bf5f1fba72d2d0640ed96971411e980f602 Simple X11App class is done. diff -r c5c33bf5f1fb -r 8769ee69bd05 include/App.h --- a/include/App.h Sat Apr 21 03:40:26 2012 -0500 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,94 +0,0 @@ -#include -#include - -#include - -//////////////////////////////////////////////////////////////////////////////// -class App - { - public: - - ///////////////////////////////////// - class BitMap - { - public: - BitMap(const std::string & file); - ~BitMap(); - - void make_pixmap(Display * dpy, Drawable d); - - // Yeah, it's all public here. Keeping it simple. - std::string name; - unsigned int width; - unsigned int height; - unsigned char * data; - Pixmap pixmap; - - private: - BitMap(const App::BitMap & b); - - int read_from_file(const std::string & file); - }; - - ///////////////////////////////////// - // TODO: Need to be able to support child windows. - // Thus, I need a tree and this class should be a node of the tree. - // I could use an STL multimap container that maps child nodes onto parent nodes - // The root node would need to be a bit special. Perhaps it would map to itself. - // Use the equal_range() method to get the children of the specified node. - // I would still need to implement my own tree walking methods: get root node, - // for each child (get children and recurse). - // - // It may be more efficient to implement the tree directly instead of using - // std::map as an intermediary. - - class WindowData - { - public: - WindowData(const std::string & n, Window w, App::BitMap * i) : - name(n), win(w), icon(i) {}; - ~WindowData() - { - if (icon) { delete icon; icon = NULL; } - } - - - std::string name; - Window win; - App::BitMap * icon; - }; - - ///////////////////////////////////// - - typedef std::pair win_pair; - typedef std::map WindowMap; - - App(const std::string & appname); - ~App(); - - static int x_error_handler(Display * dpy, XErrorEvent *err); - - void create_window(const std::string& win_name, - int x, int y, - unsigned int width, unsigned int height, - App::BitMap * icon); - - inline int display_height(void) const - { return DisplayHeight(dpy, DefaultScreen(dpy)); }; - - inline int display_width(void) const - { return DisplayWidth(dpy, DefaultScreen(dpy)); }; - - void run(void); - - private: - App(); - App(const App & a); - - Display * dpy; - WindowMap windows; - long event_mask; - Atom wm_delete_window; // Need this in contructor and run(). - - std::string button_state_string(unsigned int state) const; - }; diff -r c5c33bf5f1fb -r 8769ee69bd05 include/X11App.h --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/include/X11App.h Sun Apr 22 01:25:33 2012 -0500 @@ -0,0 +1,43 @@ +#ifndef APP_H +#define X11APP_H + +#include + +#include + +class X11Window + { + public: + X11Window(Display * dpy, + const std::string& win_name, + int x, int y, + unsigned int width, unsigned int height); + + std::string name; + Window win; + }; + +class X11App + { + public: + + X11App(const std::string & appname); + ~X11App(); + + void run(void); + + private: + X11App(); + X11App(const X11App & a); + X11App operator=(const X11App&); + + std::string button_state_string(unsigned int state); + + X11Window * main_win; + Display * dpy; + long event_mask; + Atom wm_delete_window; + + }; + +#endif diff -r c5c33bf5f1fb -r 8769ee69bd05 include/xlib_App.xbm --- a/include/xlib_App.xbm Sat Apr 21 03:40:26 2012 -0500 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,46 +0,0 @@ -#define xlib_App_width 64 -#define xlib_App_height 64 -static unsigned char xlib_App_bits[] = { - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0xc0, 0x0f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xfc, 0x0f, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0xfe, 0x1f, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x80, 0xff, 0x3f, 0x00, 0x00, 0x00, 0x10, 0x00, 0xc0, 0xff, 0x3f, 0x00, - 0x00, 0x00, 0x3c, 0x00, 0xe0, 0xff, 0x7f, 0x00, 0x00, 0x00, 0x3e, 0x00, - 0xf0, 0xff, 0x7f, 0x00, 0x00, 0x80, 0x1f, 0x00, 0xf0, 0xff, 0x7f, 0x00, - 0x00, 0x80, 0x0f, 0x00, 0xf8, 0xff, 0xff, 0x00, 0x00, 0xc0, 0x07, 0x00, - 0xfc, 0xff, 0xff, 0x01, 0x00, 0xe0, 0x03, 0x00, 0xfc, 0xf8, 0xff, 0x01, - 0x00, 0xf0, 0x01, 0x00, 0x38, 0xf0, 0xff, 0x03, 0x00, 0xf8, 0x01, 0x00, - 0x00, 0xe0, 0xff, 0x03, 0x00, 0x7c, 0x00, 0x00, 0x00, 0xe0, 0xff, 0x07, - 0x00, 0x7c, 0x00, 0x00, 0x00, 0xc0, 0xff, 0x0f, 0x00, 0x3f, 0x00, 0x00, - 0x00, 0x80, 0xff, 0x0f, 0x00, 0x0f, 0x00, 0x00, 0x00, 0x80, 0xff, 0x1f, - 0x80, 0x0f, 0x00, 0x00, 0x00, 0x00, 0xff, 0x1f, 0xc0, 0x03, 0x00, 0x00, - 0x00, 0x00, 0xff, 0x3f, 0xe0, 0x03, 0x00, 0x00, 0x00, 0x00, 0xfe, 0x3f, - 0xf0, 0x01, 0x00, 0x00, 0x00, 0x00, 0xfc, 0x7f, 0xf8, 0x00, 0x00, 0x00, - 0x00, 0x00, 0xfc, 0xff, 0x78, 0x00, 0x00, 0x00, 0x00, 0x00, 0xfc, 0xff, - 0x3c, 0x00, 0x00, 0x00, 0x00, 0x00, 0xf8, 0xff, 0x3f, 0x00, 0x00, 0x00, - 0x00, 0x00, 0xf0, 0xff, 0x1f, 0x00, 0x00, 0x00, 0x00, 0x00, 0xf0, 0xff, - 0x07, 0x00, 0x00, 0x00, 0x00, 0x00, 0xf0, 0xff, 0x07, 0x00, 0x00, 0x00, - 0x00, 0x00, 0xe0, 0xff, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0xe0, 0xff, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xe0, 0xff, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0xe0, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xe0, 0xff, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xe0, 0xff, 0x01, 0x00, 0x00, 0x00, - 0x00, 0x00, 0xe0, 0xff, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0xf8, 0xff, - 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0xfc, 0xff, 0x0f, 0x00, 0x00, 0x00, - 0x00, 0x00, 0xfe, 0xff, 0x1f, 0x00, 0x00, 0x00, 0x00, 0x80, 0xff, 0xff, - 0x1f, 0x00, 0x00, 0x00, 0x00, 0xc0, 0xcf, 0xff, 0x7f, 0x00, 0x00, 0x00, - 0x00, 0xe0, 0x87, 0xff, 0xff, 0x01, 0x00, 0x00, 0x00, 0xf0, 0x03, 0xff, - 0xff, 0x07, 0xe0, 0x00, 0x00, 0xfc, 0x00, 0xfe, 0xff, 0x1f, 0xf0, 0x00, - 0x00, 0xfe, 0x00, 0xfc, 0xff, 0xff, 0x7c, 0x00, 0x00, 0x3f, 0x00, 0xf8, - 0xff, 0xff, 0x7f, 0x00, 0x80, 0x1f, 0x00, 0xf8, 0xff, 0xff, 0x3f, 0x00, - 0xc0, 0x0f, 0x00, 0xf0, 0xff, 0xff, 0x1f, 0x00, 0xe0, 0x03, 0x00, 0xe0, - 0xff, 0xff, 0x0f, 0x00, 0xf0, 0x01, 0x00, 0xc0, 0xff, 0xff, 0x07, 0x00, - 0xf0, 0x00, 0x00, 0xc0, 0xff, 0xff, 0x03, 0x00, 0x40, 0x00, 0x00, 0xc0, - 0xff, 0xff, 0x03, 0x00, 0x00, 0x00, 0x00, 0x80, 0xff, 0xff, 0x01, 0x00, - 0x00, 0x00, 0x00, 0x00, 0xfe, 0x7f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0xf8, 0x3f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }; diff -r c5c33bf5f1fb -r 8769ee69bd05 src/App.cpp --- a/src/App.cpp Sat Apr 21 03:40:26 2012 -0500 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,356 +0,0 @@ -#include "App.h" - -#include -#include -#include -#include - - -//////////////////////////////////////////////////////////////////////////////// -App::App(const std::string & appname) - { - XSetErrorHandler(&App::x_error_handler); - - if ((dpy = XOpenDisplay(NULL)) == NULL) - { - std::cerr << "Unable to connect to display " << XDisplayName(NULL) << std::endl; - exit(1); - } - std::cout << "Display name : " << XDisplayName(NULL) << std::endl - << "Number of screens: " << ScreenCount(dpy) << std::endl - << "Default screen : " << DefaultScreen(dpy) << std::endl - << "Protocol Version : " << ProtocolVersion(dpy) << std::endl - << "Protocol Revision: " << ProtocolRevision(dpy) << std::endl - << "Server Vendor : " << ServerVendor(dpy) << std::endl - << "Vendor Release : " << VendorRelease(dpy) << std::endl - << std::endl - << "Display width : " << this->display_width() << std::endl - << "Display height : " << this->display_height() << std::endl - ; - - App::BitMap * icon = new App::BitMap("xlib_App.xbm"); - - // Create the main window - create_window("main", - 0, 0 , - this->display_width()/2, this->display_height()/2, - icon); - - // Get a reference to our WindowData structure. We need the Window handle from it for a few things. - App::WindowData * wd = windows["main"]; - - - - - // We need to set the window manager hints, size hints, window name, and icon name - - // Even though we are not passing any actual size_hints, the Xlib programming manual says to pass the flags below anyway. - XSizeHints *size_hints; - if (!(size_hints = XAllocSizeHints())) - { - std::cerr << "XAllocSizeHints: memory allocation failure" << std::endl; - exit(1); - } - size_hints->flags = PPosition | PSize; - - // Get X Properties for the window and icon names. - XTextProperty windowName, iconName; - const char * ptr = appname.c_str(); - if (XStringListToTextProperty(const_cast(&ptr), 1, &windowName) == 0) - { - std::cerr << "XStringListToTextProperty: structure allocation for windowName failed." << std::endl;; - exit(1); - } - ptr = icon->name.c_str(); - if (XStringListToTextProperty(const_cast(&ptr), 1, &iconName) == 0) - { - std::cerr << "XStringListToTextProperty: structure allocation for iconName failed." << std::endl;; - exit(1); - } - - XWMHints *wm_hints; - if (!(wm_hints = XAllocWMHints())) - { - std::cerr << "XAllocWMHints: memory allocation failure" << std::endl; - exit(0); - } - wm_hints->initial_state = NormalState; - wm_hints->input = True; - wm_hints->icon_pixmap = icon->pixmap; - wm_hints->flags = StateHint | IconPixmapHint | InputHint; - - XSetWMProperties(dpy, wd->win, &windowName, &iconName, - NULL, 0, size_hints, wm_hints, - NULL); - - - - // Select the event types we want to receive. - //Other interesting events include KeyReleaseMask and ButtonReleaseMask - event_mask = - ExposureMask | StructureNotifyMask | - KeyPressMask | KeyReleaseMask | - FocusChangeMask | - PointerMotionMask | - EnterWindowMask | LeaveWindowMask | - ButtonPressMask | ButtonReleaseMask ; - XSelectInput(dpy, wd->win, event_mask); - - - - // Make sure we get delete events from the window manager. - // "wm_delete_window" is the Atom which corresponds to the delete - // window message sent by the window manager. - - wm_delete_window = XInternAtom (dpy, "WM_DELETE_WINDOW", False); - XSetWMProtocols(dpy, wd->win, &wm_delete_window, 1); - - - // This give BadMatch and I don't know why. - // XSetWindowAttributes setwinattr; - // setwinattr.background_pixmap = bg_pixmap->pixmap; - // XChangeWindowAttributes(dpy, wd->win, CWBackPixmap, &setwinattr); - - // Also BadMatch - // XSetWindowBackgroundPixmap(dpy, wd->win, icon->pixmap); - - - // XSetWindowBackgroundPixmap(dpy, wd->win, None); - - - // Map the window. Remember: this does not display the window immediately. - // The request is queued until events are read, or we call XFlush or XSync. - XMapWindow(dpy, wd->win); - } - -//////////////////////////////////////////////////////////////////////////////// -App::~App() - { - // Ugh. I really wish I could figure out why I can't make a map of string - // to WindowData instead of having ton use WindowData * - // I'd like to eliminate this next loop. - for( WindowMap::iterator i = windows.begin(); i != windows.end(); ++i) - { - App::WindowData * wp = (*i).second; - windows.erase(i); - delete wp; - } - - if (dpy) - { - XCloseDisplay(dpy); - } - } - -//////////////////////////////////////////////////////////////////////////////// -int App::x_error_handler(Display * dpy, XErrorEvent *err) - { - char err_msg[1024]; - err_msg[0] = 0; - XGetErrorText(dpy, err->error_code, err_msg, 1024); - std::cerr << "X11 Error"<< std::endl - << " display : " << DisplayString(dpy) << std::endl - << " serial : " << err->serial << std::endl - << " error_code : " << (int) err->error_code << std::endl - << " request_code: " << (int) err->request_code << std::endl - << " minor_code : " << (int) err->minor_code << std::endl - << err_msg << std::endl; - return 0; - } - -//////////////////////////////////////////////////////////////////////////////// -void App::create_window(const std::string & win_name, - int x, int y, - unsigned int width, unsigned int height, - App::BitMap * icon) - { - Window win; - int border_width = 4; /* Border four pixels wide */ - - int screen_num = DefaultScreen(dpy); - - /* Create opaque window */ - win = XCreateSimpleWindow(dpy, - RootWindow(dpy, screen_num), - x, y, - width, height, - border_width, - None, None); - // BlackPixel(dpy, screen_num), - // WhitePixel(dpy, screen_num)); - - icon->make_pixmap(dpy, win); - - // Add it to the window list - App::WindowData * win_data = new App::WindowData(win_name, win, icon); - windows.insert(win_pair(win_name, win_data)); - } - -//////////////////////////////////////////////////////////////////////////////// -void App::run(void) - { - XEvent e; - KeySym k; - char key_buffer[8]; - key_buffer[7] = 0; - XComposeStatus compose_status; - - int done = 0; - - while (!done) - { - while (XCheckIfEvent(dpy, &e, - // *sniff* My very first lambda function in C++ - [&](Display * d, XEvent * e, XPointer a)->Bool { return True; }, - NULL)) - { - switch (e.type) - { - - //////////////////////// - // General window events - case Expose: - // We'll only redraw the entire window at a time, so unless this is - // the last contiguous expose, don't draw the window. - if (e.xexpose.count != 0) - break; - // Draw the window contents here - break; - case ClientMessage: - if ((Atom)e.xclient.data.l[0] == wm_delete_window) - done = 1; - break; - - /////////////////////// - // Mouse events - case MotionNotify: - std::cout << "Mouse at (" << e.xmotion.x << "," << e.xmotion.y << ")" << std::endl; - break; - case ButtonPress: - std::cout << "You pressed button " << e.xbutton.button << - " at (" << e.xbutton.x << "," << e.xbutton.y << ")" << - " state " << button_state_string(e.xbutton.state ) << std::endl; - break; - case ButtonRelease: - std::cout << "You released button " << e.xbutton.button << - " at (" << e.xbutton.x << "," << e.xbutton.y << ")" << - " state " << button_state_string(e.xbutton.state ) << std::endl; - break; - case EnterNotify: - std::cout << "Mouse has entered the window" << - " at (" << e.xbutton.x << "," << e.xbutton.y << ")" << - " state " << button_state_string(e.xbutton.state ) << std::endl; - break; - case LeaveNotify: - std::cout << "Mouse has left the window" << - " at (" << e.xbutton.x << "," << e.xbutton.y << ")" << - " state " << button_state_string(e.xbutton.state ) << std::endl; - break; - - ////////////////// - // Keyboard Events - case FocusIn: - // Interesting note: When we regain focus due to a mouse click in the window, - // we first get a LeaveNotify, then the FocusIn, then an EnterNotify, and then - // the ButtonPress and ButtonRelease. So in a real app we might want to have - // the FocusIn handler read ahead and handle (discard) the next ButtonPress, - // and ButtonRelease so that the click does not have a real effect on the application. - std::cout << "Got keyboard focus" << std::endl; - break; - case FocusOut: - std::cout << "Lost keyboard focus" << std::endl; - break; - case KeyPress: - // Doing the lookup like this, using XLookupString, means I get the modified - // character back. Thus type shift+a gives me XK_A instead of XK_a - // If I used XKeySym I'd instead get a sequence such as this: - // XK_Shift_L followed by XK_a - // - // In the end, that might be a more useful way to go, but for now I'll keep - // things simple. - XLookupString(&(e.xkey), key_buffer, 7, &k, &compose_status); - switch (k) - { - case XK_Escape: - case XK_q: - std::cout << "Goodbye!" << std::endl; - done = 1; - break; - default: - std::cout << "You typed " << key_buffer << std::endl; - break; - } - case MappingNotify: - // While it is uncommon for the user to change the keyboard mapping while - // a program is running, handling this case is so trivial that there is - // simply no excuse to leave it out. - XRefreshKeyboardMapping((XMappingEvent *)&e); - break; - - default: - break; - } - } - - // Do idle time things here - - } - } - -//////////////////////////////////////////////////////////////////////////////// -std::string App::button_state_string(unsigned int state) const - { - std::string str = ""; - if (state & Button1Mask) { str.append("Button1 "); } - if (state & Button2Mask) { str.append("Button2 "); } - if (state & Button3Mask) { str.append("Button3 "); } - if (state & Button4Mask) { str.append("Button4 "); } - if (state & Button5Mask) { str.append("Button5 "); } - if (state & ShiftMask) { str.append("Shift "); } - if (state & LockMask) { str.append("Lock "); } - if (state & ControlMask) { str.append("Control "); } - if (state & Mod1Mask) { str.append("Mod1 "); } - if (state & Mod2Mask) { str.append("Mod2 "); } - if (state & Mod3Mask) { str.append("Mod3 "); } - if (state & Mod4Mask) { str.append("Mod4 "); } - if (state & Mod5Mask) { str.append("Mod5 "); } - return str; - } - -//////////////////////////////////////////////////////////////////////////////// -// App::BitMap -//////////////////////////////////////////////////////////////////////////////// -App::BitMap::BitMap(const std::string & file) - { - read_from_file(file); - } - -//////////////////////////////////////////////////////////////////////////////// -App::BitMap::~BitMap() - { - if (data) { XFree(data); data = NULL; } - } - -//////////////////////////////////////////////////////////////////////////////// -void App::BitMap::make_pixmap(Display * dpy, Drawable d) - { - pixmap = XCreateBitmapFromData(dpy, d, (char *) data, width, height); - } - -//////////////////////////////////////////////////////////////////////////////// -int App::BitMap::read_from_file(const std::string & file) - { - if (BitmapSuccess != XReadBitmapFileData(file.c_str(), &width, &height, &data, NULL, NULL)) - { - return 0; - } - - name = file; - size_t p = name.rfind("/"); - if (p != std::string::npos) - { - name.replace(0, p+1, ""); - } - return 1; - } - diff -r c5c33bf5f1fb -r 8769ee69bd05 src/X11App.cpp --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/X11App.cpp Sun Apr 22 01:25:33 2012 -0500 @@ -0,0 +1,258 @@ +#include "X11App.h" + +#include +#include +#include +#include + +//////////////////////////////////////////////////////////////////////////////// +// X11Window +//////////////////////////////////////////////////////////////////////////////// +X11Window::X11Window(Display * dpy, + const std::string & win_name, + int x, int y, + unsigned int width, unsigned int height) : + name (win_name), + win (0) + { + int screen_num = DefaultScreen(dpy); + + win = XCreateSimpleWindow(dpy, + RootWindow(dpy, screen_num), + x, y, + width, height, + 1, + None, None); + + } + +//////////////////////////////////////////////////////////////////////////////// +// X11App +//////////////////////////////////////////////////////////////////////////////// +X11App::X11App(const std::string & appname) : + main_win (NULL), dpy (NULL), event_mask (0), wm_delete_window (0) + { + + if ((dpy = XOpenDisplay(NULL)) == NULL) + { + std::cerr << "Unable to connect to display " << XDisplayName(NULL) << std::endl; + exit(1); + } + std::cout << "Display name : " << XDisplayName(NULL) << std::endl + << "Number of screens: " << ScreenCount(dpy) << std::endl + << "Default screen : " << DefaultScreen(dpy) << std::endl + << "Protocol Version : " << ProtocolVersion(dpy) << std::endl + << "Protocol Revision: " << ProtocolRevision(dpy) << std::endl + << "Server Vendor : " << ServerVendor(dpy) << std::endl + << "Vendor Release : " << VendorRelease(dpy) << std::endl + << std::endl + << "Display width : " << DisplayWidth(dpy, DefaultScreen(dpy)) << std::endl + << "Display height : " << DisplayHeight(dpy, DefaultScreen(dpy)) << std::endl + ; + + // Create the main window + main_win = new X11Window(dpy, "main", 0, 0, + DisplayWidth(dpy, DefaultScreen(dpy)) / 2, + DisplayHeight(dpy, DefaultScreen(dpy)) / 2); + + + // We need to set the window manager hints, size hints, and window name. + + // Even though we are not passing any actual size_hints, the Xlib programming manual says to pass the flags below anyway. + XSizeHints *size_hints; + if (!(size_hints = XAllocSizeHints())) + { + std::cerr << "XAllocSizeHints: memory allocation failure" << std::endl; + exit(1); + } + size_hints->flags = PPosition | PSize; + + + // Get X Properties for the window name. + XTextProperty windowName; + const char * ptr = appname.c_str(); + if (XStringListToTextProperty(const_cast(&ptr), 1, &windowName) == 0) + { + std::cerr << "XStringListToTextProperty: structure allocation for windowName failed." << std::endl;; + exit(1); + } + + XWMHints *wm_hints; + if (!(wm_hints = XAllocWMHints())) + { + std::cerr << "XAllocWMHints: memory allocation failure" << std::endl; + exit(0); + } + wm_hints->initial_state = NormalState; + wm_hints->input = True; + wm_hints->flags = StateHint | InputHint; + + XSetWMProperties(dpy, main_win->win, &windowName, NULL, + NULL, 0, size_hints, wm_hints, + NULL); + + + // Select the event types we want to receive. + event_mask = + ExposureMask | StructureNotifyMask | + KeyPressMask | KeyReleaseMask | + FocusChangeMask | + PointerMotionMask | + EnterWindowMask | LeaveWindowMask | + ButtonPressMask | ButtonReleaseMask ; + XSelectInput(dpy, main_win->win, event_mask); + + + // Make sure we get delete events from the window manager. + // "wm_delete_window" is the Atom which corresponds to the delete + // window message sent by the window manager. + + wm_delete_window = XInternAtom (dpy, "WM_DELETE_WINDOW", False); + XSetWMProtocols(dpy, main_win->win, &wm_delete_window, 1); + + + // Map the window. Remember: this does not display the window immediately. + // The request is queued until events are read, or we call XFlush or XSync. + XMapWindow(dpy, main_win->win); + } + +//////////////////////////////////////////////////////////////////////////////// +X11App::~X11App() + { + if (main_win) { delete main_win; } + if (dpy) { XCloseDisplay(dpy); } + } + +//////////////////////////////////////////////////////////////////////////////// +void X11App::run(void) + { + XEvent e; + KeySym k; + char key_buffer[8]; + key_buffer[7] = 0; + XComposeStatus compose_status; + + int done = 0; + + while (!done) + { + while (XCheckIfEvent(dpy, &e, + // *sniff* My very first lambda function in C++ + [&](Display * d, XEvent * e, XPointer a)->Bool { return True; }, + NULL)) + { + switch (e.type) + { + + //////////////////////// + // General window events + case Expose: + // We'll only redraw the entire window at a time, so unless this is + // the last contiguous expose, don't draw the window. + if (e.xexpose.count != 0) + break; + // Draw the window contents here + break; + case ClientMessage: + if ((Atom)e.xclient.data.l[0] == wm_delete_window) + done = 1; + break; + + /////////////////////// + // Mouse events + case MotionNotify: + std::cout << "Mouse at (" << e.xmotion.x << "," << e.xmotion.y << ")" << std::endl; + break; + case ButtonPress: + std::cout << "You pressed button " << e.xbutton.button << + " at (" << e.xbutton.x << "," << e.xbutton.y << ")" << + " state " << button_state_string(e.xbutton.state ) << std::endl; + break; + case ButtonRelease: + std::cout << "You released button " << e.xbutton.button << + " at (" << e.xbutton.x << "," << e.xbutton.y << ")" << + " state " << button_state_string(e.xbutton.state ) << std::endl; + break; + case EnterNotify: + std::cout << "Mouse has entered the window" << + " at (" << e.xbutton.x << "," << e.xbutton.y << ")" << + " state " << button_state_string(e.xbutton.state ) << std::endl; + break; + case LeaveNotify: + std::cout << "Mouse has left the window" << + " at (" << e.xbutton.x << "," << e.xbutton.y << ")" << + " state " << button_state_string(e.xbutton.state ) << std::endl; + break; + + ////////////////// + // Keyboard Events + case FocusIn: + // Interesting note: When we regain focus due to a mouse click in the window, + // we first get a LeaveNotify, then the FocusIn, then an EnterNotify, and then + // the ButtonPress and ButtonRelease. So in a real app we might want to have + // the FocusIn handler read ahead and handle (discard) the next ButtonPress, + // and ButtonRelease so that the click does not have a real effect on the application. + std::cout << "Got keyboard focus" << std::endl; + break; + case FocusOut: + std::cout << "Lost keyboard focus" << std::endl; + break; + case KeyPress: + // Doing the lookup like this, using XLookupString, means I get the modified + // character back. Thus type shift+a gives me XK_A instead of XK_a + // If I used XKeySym I'd instead get a sequence such as this: + // XK_Shift_L followed by XK_a + // + // In the end, that might be a more useful way to go, but for now I'll keep + // things simple. + XLookupString(&(e.xkey), key_buffer, 7, &k, &compose_status); + switch (k) + { + case XK_Escape: + case XK_q: + std::cout << "Goodbye!" << std::endl; + done = 1; + break; + default: + std::cout << "You typed " << key_buffer << std::endl; + break; + } + case MappingNotify: + // While it is uncommon for the user to change the keyboard mapping while + // a program is running, handling this case is so trivial that there is + // simply no excuse to leave it out. + XRefreshKeyboardMapping((XMappingEvent *)&e); + break; + + default: + break; + } + } + + // Do idle time things here + + } + } + +//////////////////////////////////////////////////////////////////////////////// +std::string X11App::button_state_string(unsigned int state) + { + std::string str = ""; + if (state & Button1Mask) { str.append("Button1 "); } + if (state & Button2Mask) { str.append("Button2 "); } + if (state & Button3Mask) { str.append("Button3 "); } + if (state & Button4Mask) { str.append("Button4 "); } + if (state & Button5Mask) { str.append("Button5 "); } + if (state & ShiftMask) { str.append("Shift "); } + if (state & LockMask) { str.append("Lock "); } + if (state & ControlMask) { str.append("Control "); } + if (state & Mod1Mask) { str.append("Mod1 "); } + if (state & Mod2Mask) { str.append("Mod2 "); } + if (state & Mod3Mask) { str.append("Mod3 "); } + if (state & Mod4Mask) { str.append("Mod4 "); } + if (state & Mod5Mask) { str.append("Mod5 "); } + size_t i = str.rfind(' '); + if (i != str.npos) { str.erase(i, 1); } + return str; + } + diff -r c5c33bf5f1fb -r 8769ee69bd05 src/main.cpp --- a/src/main.cpp Sat Apr 21 03:40:26 2012 -0500 +++ b/src/main.cpp Sun Apr 22 01:25:33 2012 -0500 @@ -2,11 +2,11 @@ #include -#include "App.h" +#include "X11App.h" int main(void) { - App * app = new App("xlib_App"); + X11App * app = new X11App("xlib_App"); app->run(); exit(0); }