1 /*------------------------------------------------------------------------
2  *  Copyright 2007-2009 (c) Jeff Brown <spadix@users.sourceforge.net>
3  *
4  *  This file is part of the ZBar Bar Code Reader.
5  *
6  *  The ZBar Bar Code Reader is free software; you can redistribute it
7  *  and/or modify it under the terms of the GNU Lesser Public License as
8  *  published by the Free Software Foundation; either version 2.1 of
9  *  the License, or (at your option) any later version.
10  *
11  *  The ZBar Bar Code Reader is distributed in the hope that it will be
12  *  useful, but WITHOUT ANY WARRANTY; without even the implied warranty
13  *  of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14  *  GNU Lesser Public License for more details.
15  *
16  *  You should have received a copy of the GNU Lesser Public License
17  *  along with the ZBar Bar Code Reader; if not, write to the Free
18  *  Software Foundation, Inc., 51 Franklin St, Fifth Floor,
19  *  Boston, MA  02110-1301  USA
20  *
21  *  http://sourceforge.net/projects/zbar
22  *------------------------------------------------------------------------*/
23 
24 #include "window.h"
25 #include "processor.h"
26 #include "posix.h"
27 
28 #include <X11/Xlib.h>
29 #include <X11/Xutil.h>
30 #include <X11/keysym.h>
31 
x_handle_event(zbar_processor_t * proc)32 static inline int x_handle_event (zbar_processor_t *proc)
33 {
34     XEvent ev;
35     XNextEvent(proc->display, &ev);
36 
37     switch(ev.type) {
38     case Expose: {
39         /* FIXME ignore when running(?) */
40         XExposeEvent *exp = (XExposeEvent*)&ev;
41         while(1) {
42             assert(ev.type == Expose);
43             _zbar_window_expose(proc->window, exp->x, exp->y,
44                                 exp->width, exp->height);
45             if(!exp->count)
46                 break;
47             XNextEvent(proc->display, &ev);
48         }
49         zbar_window_redraw(proc->window);
50         break;
51     }
52 
53     case ConfigureNotify:
54         zprintf(3, "resized to %d x %d\n",
55                 ev.xconfigure.width, ev.xconfigure.height);
56         zbar_window_resize(proc->window,
57                            ev.xconfigure.width, ev.xconfigure.height);
58         _zbar_processor_invalidate(proc);
59         break;
60 
61     case ClientMessage:
62         if((ev.xclient.message_type ==
63             XInternAtom(proc->display, "WM_PROTOCOLS", 0)) &&
64            ev.xclient.format == 32 &&
65            (ev.xclient.data.l[0] ==
66             XInternAtom(proc->display, "WM_DELETE_WINDOW", 0))) {
67             zprintf(3, "WM_DELETE_WINDOW\n");
68             return(_zbar_processor_handle_input(proc, -1));
69         }
70         break;
71 
72     case KeyPress: {
73         KeySym key = XLookupKeysym(&ev.xkey, 0);
74         if(IsModifierKey(key))
75             break;
76         if((key & 0xff00) == 0xff00)
77             key &= 0x00ff;
78         zprintf(16, "KeyPress(%04lx)\n", key);
79         /* FIXME this doesn't really work... */
80         return(_zbar_processor_handle_input(proc, key & 0xffff));
81     }
82     case ButtonPress: {
83         zprintf(16, "ButtonPress(%d)\n", ev.xbutton.button);
84         int idx = 1;
85         switch(ev.xbutton.button) {
86         case Button2: idx = 2; break;
87         case Button3: idx = 3; break;
88         case Button4: idx = 4; break;
89         case Button5: idx = 5; break;
90         }
91         return(_zbar_processor_handle_input(proc, idx));
92     }
93 
94     case DestroyNotify:
95         zprintf(16, "DestroyNotify\n");
96         zbar_window_attach(proc->window, NULL, 0);
97         proc->xwin = 0;
98         return(0);
99 
100     default:
101         /* ignored */;
102     }
103     return(0);
104 }
105 
x_handle_events(zbar_processor_t * proc)106 static int x_handle_events (zbar_processor_t *proc)
107 {
108     int rc = 0;
109     while(rc >= 0 && XPending(proc->display))
110         rc = x_handle_event(proc);
111     return(rc);
112 }
113 
x_connection_handler(zbar_processor_t * proc,int i)114 static int x_connection_handler (zbar_processor_t *proc,
115                                  int i)
116 {
117     _zbar_mutex_lock(&proc->mutex);
118     _zbar_processor_lock(proc);
119     _zbar_mutex_unlock(&proc->mutex);
120 
121     x_handle_events(proc);
122 
123     _zbar_mutex_lock(&proc->mutex);
124     _zbar_processor_unlock(proc, 0);
125     _zbar_mutex_unlock(&proc->mutex);
126     return(0);
127 }
128 
x_internal_handler(zbar_processor_t * proc,int i)129 static int x_internal_handler (zbar_processor_t *proc,
130                                int i)
131 {
132     XProcessInternalConnection(proc->display, proc->state->polling.fds[i].fd);
133     x_connection_handler(proc, i);
134     return(0);
135 }
136 
x_internal_watcher(Display * display,XPointer client_arg,int fd,Bool opening,XPointer * watch_arg)137 static void x_internal_watcher (Display *display,
138                                 XPointer client_arg,
139                                 int fd,
140                                 Bool opening,
141                                 XPointer *watch_arg)
142 {
143     zbar_processor_t *proc = (void*)client_arg;
144     if(opening)
145         add_poll(proc, fd, x_internal_handler);
146     else
147         remove_poll(proc, fd);
148 }
149 
_zbar_processor_open(zbar_processor_t * proc,char * title,unsigned width,unsigned height)150 int _zbar_processor_open (zbar_processor_t *proc,
151                           char *title,
152                           unsigned width,
153                           unsigned height)
154 {
155     proc->display = XOpenDisplay(NULL);
156     if(!proc->display)
157         return(err_capture_str(proc, SEV_ERROR, ZBAR_ERR_XDISPLAY, __func__,
158                                "unable to open X display",
159                                XDisplayName(NULL)));
160 
161     add_poll(proc, ConnectionNumber(proc->display), x_connection_handler);
162     XAddConnectionWatch(proc->display, x_internal_watcher, (void*)proc);
163     /* must also flush X event queue before polling */
164     proc->state->pre_poll_handler = x_connection_handler;
165 
166     int screen = DefaultScreen(proc->display);
167     XSetWindowAttributes attr;
168     attr.event_mask = (ExposureMask | StructureNotifyMask |
169                        KeyPressMask | ButtonPressMask);
170 
171     proc->xwin = XCreateWindow(proc->display,
172                                RootWindow(proc->display, screen),
173                                0, 0, width, height, 0,
174                                CopyFromParent, InputOutput,
175                                CopyFromParent, CWEventMask, &attr);
176     if(!proc->xwin) {
177         XCloseDisplay(proc->display);
178         return(err_capture(proc, SEV_ERROR, ZBAR_ERR_XPROTO, __func__,
179                            "creating window"));
180     }
181 
182     XStoreName(proc->display, proc->xwin, title);
183 
184     XClassHint *class_hint = XAllocClassHint();
185     class_hint->res_name = "zbar";
186     class_hint->res_class = "zbar";
187     XSetClassHint(proc->display, proc->xwin, class_hint);
188     XFree(class_hint);
189     class_hint = NULL;
190 
191     Atom wm_delete_window = XInternAtom(proc->display, "WM_DELETE_WINDOW", 0);
192     if(wm_delete_window)
193         XSetWMProtocols(proc->display, proc->xwin, &wm_delete_window, 1);
194 
195     if(zbar_window_attach(proc->window, proc->display, proc->xwin))
196         return(err_copy(proc, proc->window));
197     return(0);
198 }
199 
_zbar_processor_close(zbar_processor_t * proc)200 int _zbar_processor_close (zbar_processor_t *proc)
201 {
202     if(proc->window)
203         zbar_window_attach(proc->window, NULL, 0);
204 
205     if(proc->display) {
206         if(proc->xwin) {
207             XDestroyWindow(proc->display, proc->xwin);
208             proc->xwin = 0;
209         }
210         proc->state->pre_poll_handler = NULL;
211         remove_poll(proc, ConnectionNumber(proc->display));
212         XCloseDisplay(proc->display);
213         proc->display = NULL;
214     }
215     return(0);
216 }
217 
_zbar_processor_invalidate(zbar_processor_t * proc)218 int _zbar_processor_invalidate (zbar_processor_t *proc)
219 {
220     if(!proc->display || !proc->xwin)
221         return(0);
222     XClearArea(proc->display, proc->xwin, 0, 0, 0, 0, 1);
223     XFlush(proc->display);
224     return(0);
225 }
226 
_zbar_processor_set_size(zbar_processor_t * proc,unsigned width,unsigned height)227 int _zbar_processor_set_size (zbar_processor_t *proc,
228                               unsigned width,
229                               unsigned height)
230 {
231     if(!proc->display || !proc->xwin)
232         return(0);
233 
234     /* refuse to resize greater than (default) screen size */
235     XWindowAttributes attr;
236     XGetWindowAttributes(proc->display, proc->xwin, &attr);
237 
238     int maxw = WidthOfScreen(attr.screen);
239     int maxh = HeightOfScreen(attr.screen);
240     int w, h;
241     if(width > maxw) {
242         h = (maxw * height + width - 1) / width;
243         w = maxw;
244     }
245     else {
246         w = width;
247         h = height;
248     }
249     if(h > maxh) {
250         w = (maxh * width + height - 1) / height;
251         h = maxh;
252     }
253     assert(w <= maxw);
254     assert(h <= maxh);
255 
256     XResizeWindow(proc->display, proc->xwin, w, h);
257     XFlush(proc->display);
258     return(0);
259 }
260 
_zbar_processor_set_visible(zbar_processor_t * proc,int visible)261 int _zbar_processor_set_visible (zbar_processor_t *proc,
262                                  int visible)
263 {
264     if(visible)
265         XMapRaised(proc->display, proc->xwin);
266     else
267         XUnmapWindow(proc->display, proc->xwin);
268     XFlush(proc->display);
269     return(0);
270 }
271