1 #include <stdio.h>
2 #include <stdlib.h>
3 #include <sys/types.h>
4 #include <sys/stat.h>
5 
6 #include "x11.h"
7 #include "util.h"
8 
9 #include "Game.h"
10 #include "UI.h"
11 
12 static int in_popup;
13 
14 static const char *pictdir;
15 
16 static Display *display;
17 static XtAppContext app;
18 static Drawable window, rootwindow;
19 static Colormap colormap;
20 static int depth;
21 static XColor white, black;
22 static Pixmap offscreen;
23 static XtIntervalId timer;
24 static GC stdgc, whitegc;
25 static int screensize;
26 
27 static Widget toplevel, field;
28 
29 /*
30  * Callback functions
31  */
32 
33 static void
popdown(Widget w,XtPointer client_data,XtPointer call_data)34 popdown(Widget w, XtPointer client_data, XtPointer call_data) {
35 	UNUSED(w);
36 	UNUSED(client_data);
37 	UNUSED(call_data);
38 	in_popup = 0;
39 }
40 
41 void
x11_popup(Widget dialog)42 x11_popup(Widget dialog) {
43 	Window temp;
44 	int tx, ty;
45 	XWindowAttributes tattr, pattr;
46 	int px, py;
47 
48 	XtRealizeWidget(XtParent(dialog));
49 	XtSetMappedWhenManaged(XtParent(dialog), FALSE);
50 	XtManageChild(dialog);
51 	in_popup = 1;
52 
53 	XTranslateCoordinates(display, XtWindow(toplevel), rootwindow,
54 			      0, 0, &tx, &ty, &temp);
55 	XGetWindowAttributes(display, XtWindow(toplevel), &tattr);
56 	XGetWindowAttributes(display, XtWindow(dialog), &pattr);
57 	px = tx + (tattr.width - pattr.width) / 2;
58 	py = ty + (tattr.height - pattr.height) / 2;
59 	XtVaSetValues(XtParent(dialog), XtNx, px, XtNy, py, NULL);
60 
61 	XtAddCallback(XtParent(dialog), XtNpopdownCallback,
62 		(XtCallbackProc) popdown, NULL);
63 	XtPopup(XtParent(dialog), XtGrabExclusive);
64 	while (in_popup || XtAppPending(app))
65 		XtAppProcessEvent(app, XtIMXEvent);
66 }
67 
68 /*
69  * Event handlers
70  */
71 
72 static void
leave_window(Widget w,XtPointer client_data,XEvent * event,Boolean * b)73 leave_window(Widget w, XtPointer client_data, XEvent *event, Boolean *b) {
74 	UNUSED(w);
75 	UNUSED(client_data);
76 	UNUSED(event);
77 	UNUSED(b);
78 
79 	UI_pause_game();
80 }
81 
82 static void
enter_window(Widget w,XtPointer client_data,XEvent * event,Boolean * b)83 enter_window(Widget w, XtPointer client_data, XEvent *event, Boolean *b) {
84 	UNUSED(w);
85 	UNUSED(client_data);
86 	UNUSED(event);
87 	UNUSED(b);
88 
89 	UI_resume_game();
90 }
91 
92 static void
redraw_window(Widget w,XtPointer client_data,XEvent * event,Boolean * b)93 redraw_window(Widget w, XtPointer client_data, XEvent *event, Boolean *b) {
94 	UNUSED(w);
95 	UNUSED(client_data);
96 	UNUSED(event);
97 	UNUSED(b);
98 
99 	UI_refresh();
100 }
101 
102 static void
button_press(Widget w,XtPointer data,XEvent * event,Boolean * b)103 button_press(Widget w, XtPointer data, XEvent *event, Boolean *b) {
104 	XButtonEvent *buttonevent = (XButtonEvent *) event;
105 
106 	UNUSED(w);
107 	UNUSED(data);
108 	UNUSED(b);
109 
110 	Game_button_press(buttonevent->x, buttonevent->y);
111 }
112 
113 static void
button_release(Widget w,XtPointer data,XEvent * event,Boolean * b)114 button_release(Widget w, XtPointer data, XEvent *event, Boolean *b) {
115 	XButtonEvent *buttonevent = (XButtonEvent *) event;
116 
117 	UNUSED(w);
118 	UNUSED(data);
119 	UNUSED(b);
120 
121 	Game_button_release(buttonevent->x, buttonevent->y);
122 }
123 
124 static void
timer_tick(XtPointer client_data,XtIntervalId * timer_id)125 timer_tick(XtPointer client_data, XtIntervalId *timer_id) {
126 	UNUSED(client_data);
127 	UNUSED(timer_id);
128 
129 	UI_restart_timer();
130 	Game_update();
131 }
132 
133 /*
134  * Cursor handling
135  */
136 
137 void
x11_set_cursor(MCursor * cursor)138 x11_set_cursor(MCursor *cursor) {
139 	XDefineCursor(display, window, cursor->cursor);
140 }
141 
142 void
x11_load_cursor(const char * name,int masked,MCursor ** cursorp)143 x11_load_cursor(const char *name, int masked, MCursor **cursorp) {
144 	MCursor *cursor;
145 	Pixmap bitmap, mask;
146 	int i, xh, yh;
147 	unsigned width, height;
148 	char file[255];
149 
150 	cursor = xalloc(sizeof *cursor);
151 
152 	sprintf(file, "%s/bitmaps/%s.xbm", pictdir, name);
153 	i = XReadBitmapFile(display, rootwindow, file,
154 			    &width, &height, &bitmap, &xh, &yh);
155 	if (i == BitmapOpenFailed)
156 		fatal("cannot open %s", file);
157 	if (masked == CURSOR_SEP_MASK) {
158 		sprintf(file, "%s/bitmaps/%s_mask.xbm", pictdir, name);
159 		i = XReadBitmapFile(display, rootwindow,
160 				    file, &width, &height, &mask, &xh, &yh);
161 		if (i == BitmapOpenFailed)
162 			fatal("cannot open %s", file);
163 	}
164 	else
165 		mask = bitmap;
166 	cursor->cursor = XCreatePixmapCursor(display, bitmap, mask,
167 					     &black, &white,
168 					     width/2, height/2);
169 	*cursorp = cursor;
170 }
171 
172 /*
173  * Pixmap handling
174  */
175 
176 void
x11_load_picture(const char * name,int trans,Picture ** pictp)177 x11_load_picture(const char *name, int trans, Picture **pictp) {
178 	Picture *pict;
179 	int i;
180 	char file[255];
181 	XpmColorSymbol symbol;
182 	Pixmap mask;
183 	XpmAttributes attr;
184 	unsigned long gcmask;
185 	XGCValues gcval;
186 
187 	pict = xalloc(sizeof *pict);
188 
189 	gcmask = GCForeground | GCBackground | GCGraphicsExposures;
190 	gcval.graphics_exposures = False;
191 	attr.valuemask = XpmCloseness | XpmReturnPixels | XpmColormap |
192 			 XpmDepth;
193 	attr.closeness = 65535;
194 	attr.colormap = colormap;
195 	attr.depth = depth;
196 	if (!trans) {
197 		symbol.name = NULL;
198 		symbol.value = "none";
199 		XtVaGetValues(field, XtNbackground, &symbol.pixel, NULL);
200 		attr.colorsymbols = &symbol;
201 		attr.numsymbols = 1;
202 		attr.valuemask |= XpmColorSymbols;
203 	}
204 	sprintf(file, "%s/pixmaps/%s.xpm", pictdir, name);
205 	i = XpmReadFileToPixmap(display, rootwindow, file, &pict->pix,
206 		&mask, &attr);
207 	if (i < 0)
208 		fatal("cannot open %s", file);
209 	pict->mask = mask;
210 	pict->gc = XCreateGC(display, offscreen, gcmask, &gcval);
211 	if (trans)
212 		XSetClipMask(display, pict->gc, mask);
213 	pict->width = attr.width;
214 	pict->height = attr.height;
215 
216 	*pictp = pict;
217 }
218 
219 void
x11_set_icon(Picture * icon)220 x11_set_icon(Picture *icon) {
221 	XtVaSetValues(toplevel, XtNiconPixmap, icon->pix,
222 		      XtNiconMask, icon->mask, NULL);
223 }
224 
225 int
x11_picture_width(Picture * pict)226 x11_picture_width(Picture *pict) {
227 	return (pict->width);
228 }
229 
230 int
x11_picture_height(Picture * pict)231 x11_picture_height(Picture *pict) {
232 	return (pict->height);
233 }
234 
235 /*
236  * Graphics operations
237  */
238 
239 void
x11_graphics_init()240 x11_graphics_init() {
241 	XGCValues gcval;
242 	unsigned long gcmask;
243 	gcmask = GCGraphicsExposures;
244 	gcval.graphics_exposures = False;
245 	stdgc = XCreateGC(display, window, gcmask, &gcval);
246 	XSetLineAttributes(display, stdgc, 2, LineSolid, CapRound, JoinMiter);
247 	XSetBackground(display, stdgc, white.pixel);
248 	XSetForeground(display, stdgc, black.pixel);
249 	whitegc = XCreateGC(display, window, gcmask, &gcval);
250 	XSetBackground(display, whitegc, white.pixel);
251 	XSetForeground(display, whitegc, white.pixel);
252 	offscreen = XCreatePixmap(display, rootwindow, screensize,
253 				  screensize, depth);
254 }
255 
256 void
x11_clear_window()257 x11_clear_window() {
258 	XFillRectangle(display, offscreen, whitegc, 0, 0,
259 		       screensize, screensize);
260 }
261 
262 void
x11_refresh_window()263 x11_refresh_window() {
264 	XCopyArea(display, offscreen, window, stdgc, 0, 0,
265 		  screensize, screensize, 0, 0);
266 }
267 
268 void
x11_draw_image(Picture * pict,int x,int y)269 x11_draw_image(Picture *pict, int x, int y) {
270 	XSetClipOrigin(display, pict->gc, x, y);
271 	XCopyArea(display, pict->pix, offscreen, pict->gc, 0, 0,
272 		  pict->width, pict->height, x, y);
273 }
274 
275 void
x11_draw_line(int x1,int y1,int x2,int y2)276 x11_draw_line(int x1, int y1, int x2, int y2) {
277 	XDrawLine(display, offscreen, stdgc, x1, y1, x2, y2);
278 }
279 
280 void
x11_draw_string(const char * str,int x,int y)281 x11_draw_string(const char *str, int x, int y) {
282 	XDrawString(display, offscreen, stdgc, x, y, str, strlen(str));
283 }
284 
285 /*
286  * Timer operations
287  */
288 
289 void
x11_start_timer(int ms)290 x11_start_timer(int ms) {
291 	timer = XtAppAddTimeOut(app, ms, timer_tick, NULL);
292 }
293 
294 void
x11_stop_timer()295 x11_stop_timer() {
296 	if (!timer)
297 		return;
298 	XtRemoveTimeOut(timer);
299 	timer = (XtIntervalId) 0;
300 }
301 
302 int
x11_timer_active()303 x11_timer_active() {
304 	return (!!timer);
305 }
306 
307 /*
308  * Main Loop
309  */
310 void
x11_main_loop()311 x11_main_loop() {
312 	XtAppMainLoop(app);
313 }
314 
315 /*
316  * Initialization
317  */
318 void
x11_initialize(int * argc,char ** argv)319 x11_initialize(int *argc, char **argv) {
320 	struct stat stats;
321 
322 	timer = (XtIntervalId) 0;
323 	toplevel = XtAppInitialize(&app, "XBill", NULL, 0, argc, argv,
324 				   NULL, NULL, 0);
325 	display = XtDisplay(toplevel);
326 
327 	if (stat(IMAGES, &stats) == 0)
328 		pictdir = IMAGES;
329 	else
330 		pictdir = ".";
331 }
332 
333 void
x11_setup_resources()334 x11_setup_resources() {
335 	XrmDatabase database;
336 
337 	database = XrmGetDatabase(display);
338 	XrmPutStringResource(&database, "*background", "#c4c4c4");
339 	XrmPutStringResource(&database, "*foreground", "#000000");
340 	XrmSetDatabase(display, database);
341 }
342 
343 void
x11_setup()344 x11_setup() {
345 	Screen *screen;
346 	XSizeHints h;
347 	Dimension winwidth, winheight;
348 
349 	XtRealizeWidget(toplevel);
350 	screen = XtScreen(toplevel);
351 	depth = DefaultDepthOfScreen(screen);
352 	rootwindow = RootWindowOfScreen(screen);
353 
354 	colormap = DefaultColormapOfScreen(screen);
355 	white.pixel = WhitePixelOfScreen(screen);
356 	XQueryColor(display, colormap, &white);
357 	black.pixel = BlackPixelOfScreen(screen);
358 	XQueryColor(display, colormap, &black);
359 
360 	XtVaGetValues(toplevel, XtNwidth, &winwidth, XtNheight, &winheight,
361 		      NULL);
362 	h.width = h.base_width = h.min_width = h.max_width = winwidth;
363 	h.height = h.base_height = h.min_height = h.max_height = winheight;
364 	h.width_inc = h.height_inc = 0;
365 	h.flags = PSize|PMaxSize|PMinSize|PBaseSize|PResizeInc;
366 	XSetWMNormalHints(display, XtWindow(toplevel), &h);
367 }
368 
369 void
x11_add_event_handlers(Widget w)370 x11_add_event_handlers(Widget w) {
371 	XtAddEventHandler(w, ButtonPressMask, FALSE, button_press, NULL);
372 	XtAddEventHandler(w, ButtonReleaseMask, FALSE, button_release, NULL);
373 	XtAddEventHandler(w, LeaveWindowMask, FALSE, leave_window, NULL);
374 	XtAddEventHandler(w, EnterWindowMask, FALSE, enter_window, NULL);
375 	XtAddEventHandler(w, ExposureMask, FALSE, redraw_window, NULL);
376 }
377 
378 Widget
x11_toplevel()379 x11_toplevel() {
380 	return toplevel;
381 }
382 
383 void
x11_set_drawingarea(Widget w,int size)384 x11_set_drawingarea(Widget w, int size) {
385 	field = w;
386 	window = XtWindow(w);
387 	screensize = size;
388 }
389