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