view src/App.cpp @ 8:c5c33bf5f1fb

Basic demo framework seems good
author Eris Caffee <discordia@eldalin.com>
date Sat, 21 Apr 2012 03:40:26 -0500
parents 713e3c5db1e1
children
line source
1 #include "App.h"
3 #include <cstdlib>
4 #include <iostream>
5 #include <typeinfo>
6 #include <X11/Xutil.h>
9 ////////////////////////////////////////////////////////////////////////////////
10 App::App(const std::string & appname)
11 {
12 XSetErrorHandler(&App::x_error_handler);
14 if ((dpy = XOpenDisplay(NULL)) == NULL)
15 {
16 std::cerr << "Unable to connect to display " << XDisplayName(NULL) << std::endl;
17 exit(1);
18 }
19 std::cout << "Display name : " << XDisplayName(NULL) << std::endl
20 << "Number of screens: " << ScreenCount(dpy) << std::endl
21 << "Default screen : " << DefaultScreen(dpy) << std::endl
22 << "Protocol Version : " << ProtocolVersion(dpy) << std::endl
23 << "Protocol Revision: " << ProtocolRevision(dpy) << std::endl
24 << "Server Vendor : " << ServerVendor(dpy) << std::endl
25 << "Vendor Release : " << VendorRelease(dpy) << std::endl
26 << std::endl
27 << "Display width : " << this->display_width() << std::endl
28 << "Display height : " << this->display_height() << std::endl
29 ;
31 App::BitMap * icon = new App::BitMap("xlib_App.xbm");
33 // Create the main window
34 create_window("main",
35 0, 0 ,
36 this->display_width()/2, this->display_height()/2,
37 icon);
39 // Get a reference to our WindowData structure. We need the Window handle from it for a few things.
40 App::WindowData * wd = windows["main"];
45 // We need to set the window manager hints, size hints, window name, and icon name
47 // Even though we are not passing any actual size_hints, the Xlib programming manual says to pass the flags below anyway.
48 XSizeHints *size_hints;
49 if (!(size_hints = XAllocSizeHints()))
50 {
51 std::cerr << "XAllocSizeHints: memory allocation failure" << std::endl;
52 exit(1);
53 }
54 size_hints->flags = PPosition | PSize;
56 // Get X Properties for the window and icon names.
57 XTextProperty windowName, iconName;
58 const char * ptr = appname.c_str();
59 if (XStringListToTextProperty(const_cast<char **>(&ptr), 1, &windowName) == 0)
60 {
61 std::cerr << "XStringListToTextProperty: structure allocation for windowName failed." << std::endl;;
62 exit(1);
63 }
64 ptr = icon->name.c_str();
65 if (XStringListToTextProperty(const_cast<char **>(&ptr), 1, &iconName) == 0)
66 {
67 std::cerr << "XStringListToTextProperty: structure allocation for iconName failed." << std::endl;;
68 exit(1);
69 }
71 XWMHints *wm_hints;
72 if (!(wm_hints = XAllocWMHints()))
73 {
74 std::cerr << "XAllocWMHints: memory allocation failure" << std::endl;
75 exit(0);
76 }
77 wm_hints->initial_state = NormalState;
78 wm_hints->input = True;
79 wm_hints->icon_pixmap = icon->pixmap;
80 wm_hints->flags = StateHint | IconPixmapHint | InputHint;
82 XSetWMProperties(dpy, wd->win, &windowName, &iconName,
83 NULL, 0, size_hints, wm_hints,
84 NULL);
88 // Select the event types we want to receive.
89 //Other interesting events include KeyReleaseMask and ButtonReleaseMask
90 event_mask =
91 ExposureMask | StructureNotifyMask |
92 KeyPressMask | KeyReleaseMask |
93 FocusChangeMask |
94 PointerMotionMask |
95 EnterWindowMask | LeaveWindowMask |
96 ButtonPressMask | ButtonReleaseMask ;
97 XSelectInput(dpy, wd->win, event_mask);
101 // Make sure we get delete events from the window manager.
102 // "wm_delete_window" is the Atom which corresponds to the delete
103 // window message sent by the window manager.
105 wm_delete_window = XInternAtom (dpy, "WM_DELETE_WINDOW", False);
106 XSetWMProtocols(dpy, wd->win, &wm_delete_window, 1);
109 // This give BadMatch and I don't know why.
110 // XSetWindowAttributes setwinattr;
111 // setwinattr.background_pixmap = bg_pixmap->pixmap;
112 // XChangeWindowAttributes(dpy, wd->win, CWBackPixmap, &setwinattr);
114 // Also BadMatch
115 // XSetWindowBackgroundPixmap(dpy, wd->win, icon->pixmap);
118 // XSetWindowBackgroundPixmap(dpy, wd->win, None);
121 // Map the window. Remember: this does not display the window immediately.
122 // The request is queued until events are read, or we call XFlush or XSync.
123 XMapWindow(dpy, wd->win);
124 }
126 ////////////////////////////////////////////////////////////////////////////////
127 App::~App()
128 {
129 // Ugh. I really wish I could figure out why I can't make a map of string
130 // to WindowData instead of having ton use WindowData *
131 // I'd like to eliminate this next loop.
132 for( WindowMap::iterator i = windows.begin(); i != windows.end(); ++i)
133 {
134 App::WindowData * wp = (*i).second;
135 windows.erase(i);
136 delete wp;
137 }
139 if (dpy)
140 {
141 XCloseDisplay(dpy);
142 }
143 }
145 ////////////////////////////////////////////////////////////////////////////////
146 int App::x_error_handler(Display * dpy, XErrorEvent *err)
147 {
148 char err_msg[1024];
149 err_msg[0] = 0;
150 XGetErrorText(dpy, err->error_code, err_msg, 1024);
151 std::cerr << "X11 Error"<< std::endl
152 << " display : " << DisplayString(dpy) << std::endl
153 << " serial : " << err->serial << std::endl
154 << " error_code : " << (int) err->error_code << std::endl
155 << " request_code: " << (int) err->request_code << std::endl
156 << " minor_code : " << (int) err->minor_code << std::endl
157 << err_msg << std::endl;
158 return 0;
159 }
161 ////////////////////////////////////////////////////////////////////////////////
162 void App::create_window(const std::string & win_name,
163 int x, int y,
164 unsigned int width, unsigned int height,
165 App::BitMap * icon)
166 {
167 Window win;
168 int border_width = 4; /* Border four pixels wide */
170 int screen_num = DefaultScreen(dpy);
172 /* Create opaque window */
173 win = XCreateSimpleWindow(dpy,
174 RootWindow(dpy, screen_num),
175 x, y,
176 width, height,
177 border_width,
178 None, None);
179 // BlackPixel(dpy, screen_num),
180 // WhitePixel(dpy, screen_num));
182 icon->make_pixmap(dpy, win);
184 // Add it to the window list
185 App::WindowData * win_data = new App::WindowData(win_name, win, icon);
186 windows.insert(win_pair(win_name, win_data));
187 }
189 ////////////////////////////////////////////////////////////////////////////////
190 void App::run(void)
191 {
192 XEvent e;
193 KeySym k;
194 char key_buffer[8];
195 key_buffer[7] = 0;
196 XComposeStatus compose_status;
198 int done = 0;
200 while (!done)
201 {
202 while (XCheckIfEvent(dpy, &e,
203 // *sniff* My very first lambda function in C++
204 [&](Display * d, XEvent * e, XPointer a)->Bool { return True; },
205 NULL))
206 {
207 switch (e.type)
208 {
210 ////////////////////////
211 // General window events
212 case Expose:
213 // We'll only redraw the entire window at a time, so unless this is
214 // the last contiguous expose, don't draw the window.
215 if (e.xexpose.count != 0)
216 break;
217 // Draw the window contents here
218 break;
219 case ClientMessage:
220 if ((Atom)e.xclient.data.l[0] == wm_delete_window)
221 done = 1;
222 break;
224 ///////////////////////
225 // Mouse events
226 case MotionNotify:
227 std::cout << "Mouse at (" << e.xmotion.x << "," << e.xmotion.y << ")" << std::endl;
228 break;
229 case ButtonPress:
230 std::cout << "You pressed button " << e.xbutton.button <<
231 " at (" << e.xbutton.x << "," << e.xbutton.y << ")" <<
232 " state " << button_state_string(e.xbutton.state ) << std::endl;
233 break;
234 case ButtonRelease:
235 std::cout << "You released button " << e.xbutton.button <<
236 " at (" << e.xbutton.x << "," << e.xbutton.y << ")" <<
237 " state " << button_state_string(e.xbutton.state ) << std::endl;
238 break;
239 case EnterNotify:
240 std::cout << "Mouse has entered the window" <<
241 " at (" << e.xbutton.x << "," << e.xbutton.y << ")" <<
242 " state " << button_state_string(e.xbutton.state ) << std::endl;
243 break;
244 case LeaveNotify:
245 std::cout << "Mouse has left the window" <<
246 " at (" << e.xbutton.x << "," << e.xbutton.y << ")" <<
247 " state " << button_state_string(e.xbutton.state ) << std::endl;
248 break;
250 //////////////////
251 // Keyboard Events
252 case FocusIn:
253 // Interesting note: When we regain focus due to a mouse click in the window,
254 // we first get a LeaveNotify, then the FocusIn, then an EnterNotify, and then
255 // the ButtonPress and ButtonRelease. So in a real app we might want to have
256 // the FocusIn handler read ahead and handle (discard) the next ButtonPress,
257 // and ButtonRelease so that the click does not have a real effect on the application.
258 std::cout << "Got keyboard focus" << std::endl;
259 break;
260 case FocusOut:
261 std::cout << "Lost keyboard focus" << std::endl;
262 break;
263 case KeyPress:
264 // Doing the lookup like this, using XLookupString, means I get the modified
265 // character back. Thus type shift+a gives me XK_A instead of XK_a
266 // If I used XKeySym I'd instead get a sequence such as this:
267 // XK_Shift_L followed by XK_a
268 //
269 // In the end, that might be a more useful way to go, but for now I'll keep
270 // things simple.
271 XLookupString(&(e.xkey), key_buffer, 7, &k, &compose_status);
272 switch (k)
273 {
274 case XK_Escape:
275 case XK_q:
276 std::cout << "Goodbye!" << std::endl;
277 done = 1;
278 break;
279 default:
280 std::cout << "You typed " << key_buffer << std::endl;
281 break;
282 }
283 case MappingNotify:
284 // While it is uncommon for the user to change the keyboard mapping while
285 // a program is running, handling this case is so trivial that there is
286 // simply no excuse to leave it out.
287 XRefreshKeyboardMapping((XMappingEvent *)&e);
288 break;
290 default:
291 break;
292 }
293 }
295 // Do idle time things here
297 }
298 }
300 ////////////////////////////////////////////////////////////////////////////////
301 std::string App::button_state_string(unsigned int state) const
302 {
303 std::string str = "";
304 if (state & Button1Mask) { str.append("Button1 "); }
305 if (state & Button2Mask) { str.append("Button2 "); }
306 if (state & Button3Mask) { str.append("Button3 "); }
307 if (state & Button4Mask) { str.append("Button4 "); }
308 if (state & Button5Mask) { str.append("Button5 "); }
309 if (state & ShiftMask) { str.append("Shift "); }
310 if (state & LockMask) { str.append("Lock "); }
311 if (state & ControlMask) { str.append("Control "); }
312 if (state & Mod1Mask) { str.append("Mod1 "); }
313 if (state & Mod2Mask) { str.append("Mod2 "); }
314 if (state & Mod3Mask) { str.append("Mod3 "); }
315 if (state & Mod4Mask) { str.append("Mod4 "); }
316 if (state & Mod5Mask) { str.append("Mod5 "); }
317 return str;
318 }
320 ////////////////////////////////////////////////////////////////////////////////
321 // App::BitMap
322 ////////////////////////////////////////////////////////////////////////////////
323 App::BitMap::BitMap(const std::string & file)
324 {
325 read_from_file(file);
326 }
328 ////////////////////////////////////////////////////////////////////////////////
329 App::BitMap::~BitMap()
330 {
331 if (data) { XFree(data); data = NULL; }
332 }
334 ////////////////////////////////////////////////////////////////////////////////
335 void App::BitMap::make_pixmap(Display * dpy, Drawable d)
336 {
337 pixmap = XCreateBitmapFromData(dpy, d, (char *) data, width, height);
338 }
340 ////////////////////////////////////////////////////////////////////////////////
341 int App::BitMap::read_from_file(const std::string & file)
342 {
343 if (BitmapSuccess != XReadBitmapFileData(file.c_str(), &width, &height, &data, NULL, NULL))
344 {
345 return 0;
346 }
348 name = file;
349 size_t p = name.rfind("/");
350 if (p != std::string::npos)
351 {
352 name.replace(0, p+1, "");
353 }
354 return 1;
355 }