view src/X11App.cpp @ 9:8769ee69bd05

Simple X11App class is done.
author Eris Caffee <discordia@eldalin.com>
date Sun, 22 Apr 2012 01:25:33 -0500
parents
children 60f59a6159ed
line source
1 #include "X11App.h"
3 #include <cstdlib>
4 #include <iostream>
5 #include <typeinfo>
6 #include <X11/Xutil.h>
8 ////////////////////////////////////////////////////////////////////////////////
9 // X11Window
10 ////////////////////////////////////////////////////////////////////////////////
11 X11Window::X11Window(Display * dpy,
12 const std::string & win_name,
13 int x, int y,
14 unsigned int width, unsigned int height) :
15 name (win_name),
16 win (0)
17 {
18 int screen_num = DefaultScreen(dpy);
20 win = XCreateSimpleWindow(dpy,
21 RootWindow(dpy, screen_num),
22 x, y,
23 width, height,
24 1,
25 None, None);
27 }
29 ////////////////////////////////////////////////////////////////////////////////
30 // X11App
31 ////////////////////////////////////////////////////////////////////////////////
32 X11App::X11App(const std::string & appname) :
33 main_win (NULL), dpy (NULL), event_mask (0), wm_delete_window (0)
34 {
36 if ((dpy = XOpenDisplay(NULL)) == NULL)
37 {
38 std::cerr << "Unable to connect to display " << XDisplayName(NULL) << std::endl;
39 exit(1);
40 }
41 std::cout << "Display name : " << XDisplayName(NULL) << std::endl
42 << "Number of screens: " << ScreenCount(dpy) << std::endl
43 << "Default screen : " << DefaultScreen(dpy) << std::endl
44 << "Protocol Version : " << ProtocolVersion(dpy) << std::endl
45 << "Protocol Revision: " << ProtocolRevision(dpy) << std::endl
46 << "Server Vendor : " << ServerVendor(dpy) << std::endl
47 << "Vendor Release : " << VendorRelease(dpy) << std::endl
48 << std::endl
49 << "Display width : " << DisplayWidth(dpy, DefaultScreen(dpy)) << std::endl
50 << "Display height : " << DisplayHeight(dpy, DefaultScreen(dpy)) << std::endl
51 ;
53 // Create the main window
54 main_win = new X11Window(dpy, "main", 0, 0,
55 DisplayWidth(dpy, DefaultScreen(dpy)) / 2,
56 DisplayHeight(dpy, DefaultScreen(dpy)) / 2);
59 // We need to set the window manager hints, size hints, and window name.
61 // Even though we are not passing any actual size_hints, the Xlib programming manual says to pass the flags below anyway.
62 XSizeHints *size_hints;
63 if (!(size_hints = XAllocSizeHints()))
64 {
65 std::cerr << "XAllocSizeHints: memory allocation failure" << std::endl;
66 exit(1);
67 }
68 size_hints->flags = PPosition | PSize;
71 // Get X Properties for the window name.
72 XTextProperty windowName;
73 const char * ptr = appname.c_str();
74 if (XStringListToTextProperty(const_cast<char **>(&ptr), 1, &windowName) == 0)
75 {
76 std::cerr << "XStringListToTextProperty: structure allocation for windowName failed." << std::endl;;
77 exit(1);
78 }
80 XWMHints *wm_hints;
81 if (!(wm_hints = XAllocWMHints()))
82 {
83 std::cerr << "XAllocWMHints: memory allocation failure" << std::endl;
84 exit(0);
85 }
86 wm_hints->initial_state = NormalState;
87 wm_hints->input = True;
88 wm_hints->flags = StateHint | InputHint;
90 XSetWMProperties(dpy, main_win->win, &windowName, NULL,
91 NULL, 0, size_hints, wm_hints,
92 NULL);
95 // Select the event types we want to receive.
96 event_mask =
97 ExposureMask | StructureNotifyMask |
98 KeyPressMask | KeyReleaseMask |
99 FocusChangeMask |
100 PointerMotionMask |
101 EnterWindowMask | LeaveWindowMask |
102 ButtonPressMask | ButtonReleaseMask ;
103 XSelectInput(dpy, main_win->win, event_mask);
106 // Make sure we get delete events from the window manager.
107 // "wm_delete_window" is the Atom which corresponds to the delete
108 // window message sent by the window manager.
110 wm_delete_window = XInternAtom (dpy, "WM_DELETE_WINDOW", False);
111 XSetWMProtocols(dpy, main_win->win, &wm_delete_window, 1);
114 // Map the window. Remember: this does not display the window immediately.
115 // The request is queued until events are read, or we call XFlush or XSync.
116 XMapWindow(dpy, main_win->win);
117 }
119 ////////////////////////////////////////////////////////////////////////////////
120 X11App::~X11App()
121 {
122 if (main_win) { delete main_win; }
123 if (dpy) { XCloseDisplay(dpy); }
124 }
126 ////////////////////////////////////////////////////////////////////////////////
127 void X11App::run(void)
128 {
129 XEvent e;
130 KeySym k;
131 char key_buffer[8];
132 key_buffer[7] = 0;
133 XComposeStatus compose_status;
135 int done = 0;
137 while (!done)
138 {
139 while (XCheckIfEvent(dpy, &e,
140 // *sniff* My very first lambda function in C++
141 [&](Display * d, XEvent * e, XPointer a)->Bool { return True; },
142 NULL))
143 {
144 switch (e.type)
145 {
147 ////////////////////////
148 // General window events
149 case Expose:
150 // We'll only redraw the entire window at a time, so unless this is
151 // the last contiguous expose, don't draw the window.
152 if (e.xexpose.count != 0)
153 break;
154 // Draw the window contents here
155 break;
156 case ClientMessage:
157 if ((Atom)e.xclient.data.l[0] == wm_delete_window)
158 done = 1;
159 break;
161 ///////////////////////
162 // Mouse events
163 case MotionNotify:
164 std::cout << "Mouse at (" << e.xmotion.x << "," << e.xmotion.y << ")" << std::endl;
165 break;
166 case ButtonPress:
167 std::cout << "You pressed button " << e.xbutton.button <<
168 " at (" << e.xbutton.x << "," << e.xbutton.y << ")" <<
169 " state " << button_state_string(e.xbutton.state ) << std::endl;
170 break;
171 case ButtonRelease:
172 std::cout << "You released button " << e.xbutton.button <<
173 " at (" << e.xbutton.x << "," << e.xbutton.y << ")" <<
174 " state " << button_state_string(e.xbutton.state ) << std::endl;
175 break;
176 case EnterNotify:
177 std::cout << "Mouse has entered the window" <<
178 " at (" << e.xbutton.x << "," << e.xbutton.y << ")" <<
179 " state " << button_state_string(e.xbutton.state ) << std::endl;
180 break;
181 case LeaveNotify:
182 std::cout << "Mouse has left the window" <<
183 " at (" << e.xbutton.x << "," << e.xbutton.y << ")" <<
184 " state " << button_state_string(e.xbutton.state ) << std::endl;
185 break;
187 //////////////////
188 // Keyboard Events
189 case FocusIn:
190 // Interesting note: When we regain focus due to a mouse click in the window,
191 // we first get a LeaveNotify, then the FocusIn, then an EnterNotify, and then
192 // the ButtonPress and ButtonRelease. So in a real app we might want to have
193 // the FocusIn handler read ahead and handle (discard) the next ButtonPress,
194 // and ButtonRelease so that the click does not have a real effect on the application.
195 std::cout << "Got keyboard focus" << std::endl;
196 break;
197 case FocusOut:
198 std::cout << "Lost keyboard focus" << std::endl;
199 break;
200 case KeyPress:
201 // Doing the lookup like this, using XLookupString, means I get the modified
202 // character back. Thus type shift+a gives me XK_A instead of XK_a
203 // If I used XKeySym I'd instead get a sequence such as this:
204 // XK_Shift_L followed by XK_a
205 //
206 // In the end, that might be a more useful way to go, but for now I'll keep
207 // things simple.
208 XLookupString(&(e.xkey), key_buffer, 7, &k, &compose_status);
209 switch (k)
210 {
211 case XK_Escape:
212 case XK_q:
213 std::cout << "Goodbye!" << std::endl;
214 done = 1;
215 break;
216 default:
217 std::cout << "You typed " << key_buffer << std::endl;
218 break;
219 }
220 case MappingNotify:
221 // While it is uncommon for the user to change the keyboard mapping while
222 // a program is running, handling this case is so trivial that there is
223 // simply no excuse to leave it out.
224 XRefreshKeyboardMapping((XMappingEvent *)&e);
225 break;
227 default:
228 break;
229 }
230 }
232 // Do idle time things here
234 }
235 }
237 ////////////////////////////////////////////////////////////////////////////////
238 std::string X11App::button_state_string(unsigned int state)
239 {
240 std::string str = "";
241 if (state & Button1Mask) { str.append("Button1 "); }
242 if (state & Button2Mask) { str.append("Button2 "); }
243 if (state & Button3Mask) { str.append("Button3 "); }
244 if (state & Button4Mask) { str.append("Button4 "); }
245 if (state & Button5Mask) { str.append("Button5 "); }
246 if (state & ShiftMask) { str.append("Shift "); }
247 if (state & LockMask) { str.append("Lock "); }
248 if (state & ControlMask) { str.append("Control "); }
249 if (state & Mod1Mask) { str.append("Mod1 "); }
250 if (state & Mod2Mask) { str.append("Mod2 "); }
251 if (state & Mod3Mask) { str.append("Mod3 "); }
252 if (state & Mod4Mask) { str.append("Mod4 "); }
253 if (state & Mod5Mask) { str.append("Mod5 "); }
254 size_t i = str.rfind(' ');
255 if (i != str.npos) { str.erase(i, 1); }
256 return str;
257 }