1 /***********************************************************/
2 /*                                                         */
3 /*  System dependent application management (unix, x11)    */
4 /*                                                         */
5 /***********************************************************/
6 
7 #include "apricot.h"
8 #include "unix/guts.h"
9 #include "Application.h"
10 #include <sys/utsname.h>
11 #include <sys/types.h>
12 #include <sys/socket.h>
13 #include <signal.h>
14 #if !defined(BYTEORDER)
15 #error "BYTEORDER is not defined"
16 #endif
17 #define LSB32   0x1234
18 #define LSB64   0x12345678
19 #define MSB32   0x4321
20 #define MSB64   0x87654321
21 #ifndef BUFSIZ
22 #define BUFSIZ  2048
23 #endif
24 
25 UnixGuts guts, *pguts = &guts;
26 static XErrorEvent *save_xerror_event = NULL;
27 
28 UnixGuts *
prima_unix_guts(void)29 prima_unix_guts(void)
30 {
31 	return &guts;
32 }
33 
34 static int
x_error_handler(Display * d,XErrorEvent * ev)35 x_error_handler( Display *d, XErrorEvent *ev)
36 {
37 	int tail = guts. ri_tail;
38 	int prev = tail;
39 	char *name = "Prima";
40 	char buf[BUFSIZ];
41 	char mesg[BUFSIZ];
42 	char number[32];
43 
44 	if ( save_xerror_event ) {
45 		*save_xerror_event = *ev;
46 		save_xerror_event = NULL;
47 		return 0;
48 	}
49 
50 	while ( tail != guts. ri_head) {
51 		if ( guts. ri[ tail]. request > ev-> serial)
52 			break;
53 		prev = tail;
54 		tail++;
55 		if ( tail >= REQUEST_RING_SIZE)
56 			tail = 0;
57 	}
58 
59 	switch ( ev-> request_code) {
60 	case 38: /* X_QueryPointer - apc_event uses sequence of XQueryPointer calls,
61 					to find where the pointer belongs. The error is raised when one
62 					of the windows disappears . */
63 	case 42: /* X_SetInputFocus */
64 		return 0;
65 	}
66 
67 #ifdef NEED_X11_EXTENSIONS_XRENDER_H
68 	if ( ev-> request_code == guts. xft_xrender_major_opcode &&
69 		ev-> request_code > 127 &&
70 		ev-> error_code == BadLength)
71 		/* Xrender large polygon request failed */
72 		guts. xft_disable_large_fonts = 1;
73 #endif
74 
75 #ifdef HAVE_X11_EXTENSIONS_XCOMPOSITE_H
76 	if ( ev-> request_code == guts. composite_opcode &&
77 		ev->minor_code == X_CompositeRedirectSubwindows) {
78 		/* checking for composite manager */
79 		guts. composite_error_triggered = true;
80 		return 0;
81 	}
82 #endif
83 
84 	XGetErrorText( d, ev-> error_code, buf, BUFSIZ);
85 	XGetErrorDatabaseText( d, name, "XError", "X Error", mesg, BUFSIZ);
86 	fprintf( stderr, "%s: %s, request: %d", mesg, buf, ev->request_code);
87 	if ( ev->request_code < 128) {
88 		sprintf( number, "%d", ev->request_code);
89 		XGetErrorDatabaseText( d, "XRequest", number, "", buf, BUFSIZ);
90 		fprintf( stderr, "(%s)", buf);
91 	}
92 	if ( tail == guts. ri_head && prev == guts. ri_head);
93 	else if ( tail == guts. ri_head)
94 		fprintf( stderr, ", after %s:%d\n",
95 					guts. ri[ prev]. file, guts. ri[ prev]. line);
96 	else
97 		fprintf( stderr, ", between %s:%d and %s:%d\n",
98 					guts. ri[ prev]. file, guts. ri[ prev]. line,
99 					guts. ri[ tail]. file, guts. ri[ tail]. line);
100 	return 0;
101 }
102 
103 void
prima_save_xerror_event(XErrorEvent * xr)104 prima_save_xerror_event( XErrorEvent *xr)
105 {
106 	bzero( xr, sizeof(XErrorEvent));
107 	save_xerror_event = xr;
108 }
109 
110 void
prima_restore_xerror_event(XErrorEvent * xr)111 prima_restore_xerror_event( XErrorEvent *xr)
112 {
113 	save_xerror_event = NULL;
114 	if ( xr && xr->display != NULL) x_error_handler( xr-> display, xr);
115 }
116 
117 
118 static int
x_io_error_handler(Display * d)119 x_io_error_handler( Display *d)
120 {
121 	fprintf( stderr, "Fatal input/output X error\n");
122 	_exit( 1);
123 	return 0; /* happy now? */
124 }
125 
126 static XrmDatabase
get_database(void)127 get_database( void)
128 {
129 	XrmDatabase db = XrmGetStringDatabase( "");
130 	char filename[PATH_MAX];
131 	char *c;
132 	char *resource_data = XResourceManagerString( DISP);
133 	if ( resource_data) {
134 		XrmCombineDatabase( XrmGetStringDatabase( resource_data), &db, false);
135 	} else {
136 		c = getenv( "HOME");
137 		if (!c) c = "";
138 		snprintf( filename, PATH_MAX, "%s/.Xdefaults", c);
139 		XrmCombineFileDatabase( filename, &db, false);
140 	}
141 	return db;
142 }
143 
144 static int
get_idepth(void)145 get_idepth( void)
146 {
147 	int i, n;
148 	XPixmapFormatValues *format = XListPixmapFormats( DISP, &n);
149 	int idepth = guts.depth;
150 
151 	if ( !format) return guts.depth;
152 
153 	for ( i = 0; i < n; i++)
154 		if ( format[i]. depth == guts. depth) {
155 			idepth = format[i]. bits_per_pixel;
156 			break;
157 		}
158 	XFree( format);
159 	return idepth;
160 }
161 
162 static Bool  do_x11     = true;
163 static Bool  do_sync    = false;
164 static char* do_display = NULL;
165 static int   do_debug   = 0;
166 static Bool  do_icccm_only = false;
167 static Bool  do_no_shmem   = false;
168 static Bool  do_no_gtk     = false;
169 static Bool  do_no_quartz  = false;
170 static Bool  do_no_xrender = false;
171 
172 static Bool
init_x11(char * error_buf)173 init_x11( char * error_buf )
174 {
175 	/*XXX*/ /* Namely, support for -display host:0.0 etc. */
176 	XrmQuark common_quarks_list[20];  /*XXX change number of elements if necessary */
177 	XrmQuarkList ql = common_quarks_list;
178 	XGCValues gcv;
179 	char *common_quarks =
180 		"String."
181 		"Blinkinvisibletime.blinkinvisibletime."
182 		"Blinkvisibletime.blinkvisibletime."
183 		"Clicktimeframe.clicktimeframe."
184 		"Doubleclicktimeframe.doubleclicktimeframe."
185 		"Wheeldown.wheeldown."
186 		"Wheelup.wheelup."
187 		"Submenudelay.submenudelay."
188 		"Scrollfirst.scrollfirst."
189 		"Scrollnext.scrollnext";
190 
191 	char * atom_names[AI_count] = {
192 		"RESOLUTION_X",
193 		"RESOLUTION_Y",
194 		"PIXEL_SIZE",
195 		"SPACING",
196 		"RELATIVE_WEIGHT",
197 		"FOUNDRY",
198 		"AVERAGE_WIDTH",
199 		"CHARSET_REGISTRY",
200 		"CHARSET_ENCODING",
201 		"CREATE_EVENT",
202 		"WM_DELETE_WINDOW",
203 		"WM_PROTOCOLS",
204 		"WM_TAKE_FOCUS",
205 		"_NET_WM_STATE",
206 		"_NET_WM_STATE_SKIP_TASKBAR",
207 		"_NET_WM_STATE_MAXIMIZED_VERT",
208 		"_NET_WM_STATE_MAXIMIZED_HORZ",
209 		"_NET_WM_NAME",
210 		"_NET_WM_ICON_NAME",
211 		"UTF8_STRING",
212 		"TARGETS",
213 		"INCR",
214 		"PIXEL",
215 		"FOREGROUND",
216 		"BACKGROUND",
217 		"_MOTIF_WM_HINTS",
218 		"_NET_WM_STATE_MODAL",
219 		"_NET_SUPPORTED",
220 		"_NET_WM_STATE_MAXIMIZED_HORIZ",
221 		"text/plain;charset=utf-8",
222 		"_NET_WM_STATE_STAYS_ON_TOP",
223 		"_NET_CURRENT_DESKTOP",
224 		"_NET_WORKAREA",
225 		"_NET_WM_STATE_ABOVE",
226 		"XdndEnter",
227 		"XdndPosition",
228 		"XdndStatus",
229 		"XdndTypeList",
230 		"XdndActionCopy",
231 		"XdndDrop",
232 		"XdndLeave",
233 		"XdndFinished",
234 		"XdndSelection",
235 		"XdndProxy",
236 		"XdndAware",
237 		"XdndActionMove",
238 		"XdndActionLink",
239 		"XdndActionAsk",
240 		"XdndActionPrivate",
241 		"XdndActionList",
242 	 	"XdndActionDescription",
243 		"text/plain"
244 	};
245 	char hostname_buf[256], *hostname = hostname_buf, *env;
246 
247 	guts. click_time_frame = 200;
248 	guts. double_click_time_frame = 200;
249 	guts. visible_timeout = 500;
250 	guts. invisible_timeout = 500;
251 	guts. insert = true;
252 	guts. last_time = CurrentTime;
253 
254 	guts. ri_head = guts. ri_tail = 0;
255 	DISP = XOpenDisplay( do_display);
256 
257 	if (!DISP) {
258 		char * disp = getenv("DISPLAY");
259 		snprintf( error_buf, 256, "Error: Can't open display '%s'",
260 					do_display ? do_display : (disp ? disp : ""));
261 		free( do_display);
262 		do_display = NULL;
263 		return false;
264 	}
265 	free( do_display);
266 	do_display = NULL;
267 	XSetErrorHandler( x_error_handler);
268 	guts.main_error_handler = x_error_handler;
269 	(void)x_io_error_handler;
270 	XCHECKPOINT;
271 	guts.connection = ConnectionNumber( DISP);
272 
273 	{
274 		struct sockaddr name;
275 		socklen_t l = sizeof( name);
276 		guts. local_connection = getsockname( guts.connection, &name, &l) >= 0 && l == 0;
277 	}
278 
279 #ifdef HAVE_X11_EXTENSIONS_SHAPE_H
280 	if ( XShapeQueryExtension( DISP, &guts.shape_event, &guts.shape_error)) {
281 		guts. shape_extension = true;
282 	} else {
283 		guts. shape_extension = false;
284 	}
285 #else
286 	guts. shape_extension = false;
287 #endif
288 #ifdef USE_MITSHM
289 	if ( !do_no_shmem && XShmQueryExtension( DISP)) {
290 		guts. shared_image_extension = true;
291 		guts. shared_image_completion_event = XShmGetEventBase( DISP) + ShmCompletion;
292 	} else {
293 		guts. shared_image_extension = false;
294 		guts. shared_image_completion_event = -1;
295 	}
296 #else
297 	guts. shared_image_extension = false;
298 	guts. shared_image_completion_event = -1;
299 #endif
300 	guts. randr_extension = false;
301 #ifdef HAVE_X11_EXTENSIONS_XRANDR_H
302 	{
303 		int dummy, major, minor;
304 		if ( XRRQueryExtension( DISP, &dummy, &dummy)) {
305 			if ( XRRQueryVersion( DISP, &major, &minor)) {
306 				if ( major > 1 || (major == 1 && minor > 2 )) {
307 					/* XRRGetScreenResourcesCurrent appeared in 1.3 */
308 					guts. randr_extension = true;
309 				}
310 			}
311 		}
312 	}
313 #endif
314 #ifdef HAVE_X11_EXTENSIONS_XCOMPOSITE_H
315 	{
316 		int dummy;
317 		if (XQueryExtension(DISP, COMPOSITE_NAME, &guts.composite_opcode, &dummy, &dummy))
318 			guts. composite_extension = true;
319 	}
320 #endif
321 	XrmInitialize();
322 	guts.db = get_database();
323 	XrmStringToQuarkList( common_quarks, common_quarks_list);
324 	guts.qString = *ql++;
325 	guts.qBlinkinvisibletime = *ql++;
326 	guts.qblinkinvisibletime = *ql++;
327 	guts.qBlinkvisibletime = *ql++;
328 	guts.qblinkvisibletime = *ql++;
329 	guts.qClicktimeframe = *ql++;
330 	guts.qclicktimeframe = *ql++;
331 	guts.qDoubleclicktimeframe = *ql++;
332 	guts.qdoubleclicktimeframe = *ql++;
333 	guts.qWheeldown = *ql++;
334 	guts.qwheeldown = *ql++;
335 	guts.qWheelup = *ql++;
336 	guts.qwheelup = *ql++;
337 	guts.qSubmenudelay = *ql++;
338 	guts.qsubmenudelay = *ql++;
339 	guts.qScrollfirst = *ql++;
340 	guts.qscrollfirst = *ql++;
341 	guts.qScrollnext = *ql++;
342 	guts.qscrollnext = *ql++;
343 
344 	guts. mouse_buttons = XGetPointerMapping( DISP, guts. buttons_map, 256);
345 	XCHECKPOINT;
346 
347 	guts. limits. request_length = XMaxRequestSize( DISP);
348 	guts. limits. XDrawLines = guts. limits. request_length - 3;
349 	guts. limits. XFillPolygon = guts. limits. request_length - 4;
350 	guts. limits. XDrawSegments = (guts. limits. request_length - 3) / 2;
351 	guts. limits. XDrawRectangles = (guts. limits. request_length - 3) / 2;
352 	guts. limits. XFillRectangles = (guts. limits. request_length - 3) / 2;
353 	guts. limits. XFillArcs =
354 		guts. limits. XDrawArcs = (guts. limits. request_length - 3) / 3;
355 	XCHECKPOINT;
356 	SCREEN = DefaultScreen( DISP);
357 
358 	/* XXX - return code? */
359 	guts. root = RootWindow( DISP, SCREEN);
360 	guts. displaySize. x = DisplayWidth( DISP, SCREEN);
361 	guts. displaySize. y = DisplayHeight( DISP, SCREEN);
362 #ifdef HAVE_X11_XCURSOR_XCURSOR_H
363 	guts. cursor_width = guts. cursor_height = XcursorGetDefaultSize(DISP);
364 #else
365 	XQueryBestCursor( DISP, guts. root,
366 							guts. displaySize. x,     /* :-) */
367 							guts. displaySize. y,
368 							&guts. cursor_width,
369 							&guts. cursor_height);
370 #endif
371 	XCHECKPOINT;
372 
373 	TAILQ_INIT( &guts.paintq);
374 	TAILQ_INIT( &guts.peventq);
375 	TAILQ_INIT( &guts.bitmap_gc_pool);
376 	TAILQ_INIT( &guts.screen_gc_pool);
377 	TAILQ_INIT( &guts.argb_gc_pool);
378 
379 	guts. currentFocusTime = CurrentTime;
380 	guts. windows = hash_create();
381 	guts. menu_windows = hash_create();
382 	guts. ximages = hash_create();
383 	gcv. graphics_exposures = false;
384 	guts. menugc = XCreateGC( DISP, guts. root, GCGraphicsExposures, &gcv);
385 	guts. resolution. x = ( DisplayWidthMM( DISP, SCREEN) > 0) ?
386 		25.4 * guts. displaySize. x / DisplayWidthMM( DISP, SCREEN) + .5:
387 		96;
388 	guts. resolution. y = ( DisplayHeightMM( DISP, SCREEN) > 0) ?
389 		25.4 * DisplayHeight( DISP, SCREEN) / DisplayHeightMM( DISP, SCREEN) + .5:
390 		96;
391 	guts. depth = DefaultDepth( DISP, SCREEN);
392 	guts. idepth = get_idepth();
393 	if ( guts.depth == 1) guts. qdepth = 1; else
394 	if ( guts.depth <= 4) guts. qdepth = 4; else
395 	if ( guts.depth <= 8) guts. qdepth = 8; else
396 		guts. qdepth = 24;
397 	guts. byte_order = ImageByteOrder( DISP);
398 	guts. bit_order = BitmapBitOrder( DISP);
399 	if ( BYTEORDER == LSB32 || BYTEORDER == LSB64)
400 		guts. machine_byte_order = LSBFirst;
401 	else if ( BYTEORDER == MSB32 || BYTEORDER == MSB64)
402 		guts. machine_byte_order = MSBFirst;
403 	else {
404 		sprintf( error_buf, "UAA_001: weird machine byte order: %08x", BYTEORDER);
405 		return false;
406 	}
407 
408 	XInternAtoms( DISP, atom_names, AI_count, 0, guts. atoms);
409 
410 	guts. null_pointer = NULL_HANDLE;
411 	guts. pointer_invisible_count = 0;
412 	guts. files = plist_create( 16, 16);
413 	prima_rebuild_watchers();
414 	guts. wm_event_timeout = 100;
415 	guts. menu_timeout = 200;
416 	guts. scroll_first = 200;
417 	guts. scroll_next = 50;
418 	apc_timer_create( CURSOR_TIMER);
419 	apc_timer_set_timeout(CURSOR_TIMER, 2);
420 	apc_timer_create( MENU_TIMER);
421 	apc_timer_set_timeout( MENU_TIMER,  guts. menu_timeout);
422 	apc_timer_create( MENU_UNFOCUS_TIMER);
423 	apc_timer_set_timeout( MENU_UNFOCUS_TIMER, 50);
424 	if ( !prima_init_clipboard_subsystem (error_buf)) return false;
425 	if ( !prima_init_color_subsystem     (error_buf)) return false;
426 	if ( !do_no_xrender)
427 		if ( !prima_init_xrender_subsystem(error_buf)) return false;
428 	if ( !prima_init_font_subsystem      (error_buf)) return false;
429 #ifdef WITH_GTK
430 	guts. use_gtk = do_no_gtk ? false : ( prima_gtk_init() != NULL );
431 #endif
432 #ifdef WITH_COCOA
433 	if ( prima_cocoa_is_x11_local())
434 		guts. use_quartz = !do_no_quartz;
435 #endif
436 	bzero( &guts. cursor_gcv, sizeof( guts. cursor_gcv));
437 	guts. cursor_gcv. cap_style = CapButt;
438 	guts. cursor_gcv. function = GXcopy;
439 
440 	gethostname( hostname, 256);
441 	hostname[255] = '\0';
442 	XStringListToTextProperty((char **)&hostname, 1, &guts. hostname);
443 
444 	guts. net_wm_maximization = prima_wm_net_state_read_maximization( guts. root, NET_SUPPORTED);
445 
446 	env = getenv("XDG_SESSION_TYPE");
447 	if (( env != NULL) && (strcmp(env, "wayland") == 0)) {
448 		guts. is_xwayland = true;
449 		Mdebug("XWayland detected\n");
450 	}
451 
452 	if ( do_sync) XSynchronize( DISP, true);
453 	return true;
454 }
455 
456 Bool
window_subsystem_init(char * error_buf)457 window_subsystem_init( char * error_buf)
458 {
459 	bzero( &guts, sizeof( guts));
460 	guts. debug = do_debug;
461 	guts. icccm_only = do_icccm_only;
462 	Mdebug("init x11:%d, debug:%x, sync:%d, display:%s\n", do_x11, guts.debug,
463 			do_sync, do_display ? do_display : "(default)");
464 	if ( do_x11) {
465 		Bool ret = init_x11( error_buf );
466 		if ( !ret && DISP) {
467 			XCloseDisplay(DISP);
468 			DISP = NULL;
469 		}
470 		return ret;
471 	}
472 	return true;
473 }
474 
475 int
prima_debug(const char * format,...)476 prima_debug( const char *format, ...)
477 {
478 	int rc = 0;
479 	va_list args;
480 	va_start( args, format);
481 	rc = vfprintf( stderr, format, args);
482 	va_end( args);
483 	return rc;
484 }
485 
486 Bool
window_subsystem_get_options(int * argc,char *** argv)487 window_subsystem_get_options( int * argc, char *** argv)
488 {
489 	static char * x11_argv[] = {
490 	"no-x11", "runs Prima without X11 display initialized",
491 	"display", "selects X11 DISPLAY (--display=:0.0)",
492 	"visual", "X visual id (--visual=0x21, run `xdpyinfo` for list of supported visuals)",
493 	"sync", "synchronize X connection",
494 	"icccm", "do not use NET_WM (kde/gnome) and MOTIF extensions, ICCCM only",
495 	"debug", "turns on debugging on subsystems, selected by characters (--debug=FC). "\
496 				"Recognized characters are: "\
497 				" 0(none),"\
498 				" C(clipboard),"\
499 				" E(events),"\
500 				" F(fonts),"\
501 				" M(miscellaneous),"\
502 				" P(palettes and colors),"\
503 				" X(XRDB),"\
504 				" A(all together)",
505 #ifdef USE_MITSHM
506 	"no-shmem",       "do not use shared memory for images",
507 #endif
508 	"no-core-fonts", "do not use core fonts",
509 #ifdef USE_XFT
510 	"no-xft",        "do not use XFT",
511 	"no-aa",         "do not anti-alias XFT fonts",
512 	"font-priority", "match unknown fonts against: 'xft' (default) or 'core'",
513 #endif
514 #ifdef WITH_GTK
515 	"no-gtk",        "do not use GTK",
516 #endif
517 #ifdef WITH_HARFBUZZ
518 	"no-harfbuzz",   "do not use harfbuzz",
519 #endif
520 #ifdef WITH_COCOA
521 	"no-quartz",     "do not use Quartz",
522 #endif
523 #ifdef HAVE_X11_EXTENSIONS_XRENDER_H
524 	"no-xrender",    "do not use XRender",
525 #endif
526 	"font",
527 #ifdef USE_XFT
528 				"default prima font in XLFD (-helv-misc-*-*-) or XFT(Helv-12) format",
529 #else
530 				"default prima font in XLFD (-helv-misc-*-*-) format",
531 #endif
532 	"menu-font", "default menu font",
533 	"msg-font", "default message box font",
534 	"widget-font", "default widget font",
535 	"caption-font", "MDI caption font",
536 	"noscaled", "do not use scaled instances of fonts",
537 	"fg", "default foreground color",
538 	"bg", "default background color",
539 	"hilite-fg", "default highlight foreground color",
540 	"hilite-bg", "default highlight background color",
541 	"disabled-fg", "default disabled foreground color",
542 	"disabled-bg", "default disabled background color",
543 	"light", "default light-3d color",
544 	"dark", "default dark-3d color"
545 	};
546 	*argv = x11_argv;
547 	*argc = sizeof( x11_argv) / sizeof( char*);
548 	return true;
549 }
550 
551 Bool
window_subsystem_set_option(char * option,char * value)552 window_subsystem_set_option( char * option, char * value)
553 {
554 	Mdebug("%s=%s\n", option, value);
555 	if ( strcmp( option, "no-x11") == 0) {
556 		if ( value) warn("`--no-x11' option has no parameters");
557 		do_x11 = false;
558 		return true;
559 	} else if ( strcmp( option, "yes-x11") == 0) {
560 		do_x11 = true;
561 		return true;
562 	} else if ( strcmp( option, "display") == 0) {
563 		free( do_display);
564 		do_display = duplicate_string( value);
565 		setenv("DISPLAY", value, 1);
566 		return true;
567 	} else if ( strcmp( option, "icccm") == 0) {
568 		if ( value) warn("`--icccm' option has no parameters");
569 		do_icccm_only = true;
570 		return true;
571 	} else if ( strcmp( option, "no-shmem") == 0) {
572 		if ( value) warn("`--no-shmem' option has no parameters");
573 		do_no_shmem = true;
574 		return true;
575 	} else if ( strcmp( option, "no-gtk") == 0) {
576 		if ( value) warn("`--no-gtk' option has no parameters");
577 		do_no_gtk = true;
578 		return true;
579 	} else if ( strcmp( option, "no-quartz") == 0) {
580 		if ( value) warn("`--no-quartz' option has no parameters");
581 		do_no_quartz = true;
582 		return true;
583 	} else if ( strcmp( option, "no-xrender") == 0) {
584 		if ( value) warn("`--no-xrender' option has no parameters");
585 		do_no_xrender = true;
586 		return true;
587 	} else if ( strcmp( option, "debug") == 0) {
588 		if ( !value) {
589 			warn("`--debug' must be given parameters. `--debug=A` assumed\n");
590 			guts. debug |= DEBUG_ALL;
591 			do_debug = guts. debug;
592 			return true;
593 		}
594 		while ( *value) switch ( tolower(*(value++))) {
595 		case '0':
596 			guts. debug = 0;
597 			break;
598 		case 'c':
599 			guts. debug |= DEBUG_CLIP;
600 			break;
601 		case 'e':
602 			guts. debug |= DEBUG_EVENT;
603 			break;
604 		case 'f':
605 			guts. debug |= DEBUG_FONTS;
606 			break;
607 		case 'm':
608 			guts. debug |= DEBUG_MISC;
609 			break;
610 		case 'p':
611 			guts. debug |= DEBUG_COLOR;
612 			break;
613 		case 'x':
614 			guts. debug |= DEBUG_XRDB;
615 			break;
616 		case 'a':
617 			guts. debug |= DEBUG_ALL;
618 			break;
619 		}
620 		do_debug = guts. debug;
621 	} else if ( prima_font_subsystem_set_option( option, value)) {
622 		return true;
623 	} else if ( prima_color_subsystem_set_option( option, value)) {
624 		return true;
625 	}
626 	return false;
627 }
628 
629 void
window_subsystem_cleanup(void)630 window_subsystem_cleanup( void)
631 {
632 	if ( !DISP) return;
633 	/*XXX*/
634 	prima_end_menu();
635 #ifdef WITH_GTK
636 	if ( guts. use_gtk) prima_gtk_done();
637 #endif
638 }
639 
640 static void
free_gc_pool(struct gc_head * head)641 free_gc_pool( struct gc_head *head)
642 {
643 	GCList *n1, *n2;
644 
645 	n1 = TAILQ_FIRST(head);
646 	while (n1 != NULL) {
647 		n2 = TAILQ_NEXT(n1, gc_link);
648 		XFreeGC( DISP, n1-> gc);
649 		/* XXX */ free(n1);
650 		n1 = n2;
651 	}
652 	TAILQ_INIT(head);
653 }
654 
655 void
window_subsystem_done(void)656 window_subsystem_done( void)
657 {
658 	int i;
659 	if ( !DISP) return;
660 
661 	for ( i = 0; i < sizeof(guts.xdnd_pointers) / sizeof(CustomPointer); i++) {
662 		CustomPointer *cp = guts.xdnd_pointers + i;
663 		if ( cp-> cursor )
664 			XFreeCursor( DISP, cp->cursor);
665 #ifdef HAVE_X11_XCURSOR_XCURSOR_H
666 		if ( cp-> xcursor != NULL)
667 			XcursorImageDestroy(cp-> xcursor);
668 #endif
669 	}
670 
671 	if ( guts. hostname. value) {
672 		XFree( guts. hostname. value);
673 		guts. hostname. value = NULL;
674 	}
675 
676 	prima_end_menu();
677 
678 	free_gc_pool(&guts.bitmap_gc_pool);
679 	free_gc_pool(&guts.screen_gc_pool);
680 	free_gc_pool(&guts.argb_gc_pool);
681 	prima_done_color_subsystem();
682 	prima_done_xrender_subsystem();
683 	free( guts. clipboard_formats);
684 
685 	XFreeGC( DISP, guts. menugc);
686 	prima_gc_ximages();          /* verrry dangerous, very quiet please */
687 	if ( guts.pointer_font) {
688 		XFreeFont( DISP, guts.pointer_font);
689 		guts.pointer_font = NULL;
690 	}
691 	XCloseDisplay( DISP);
692 	DISP = NULL;
693 
694 	plist_destroy( guts. files);
695 	guts. files = NULL;
696 
697 	XrmDestroyDatabase( guts.db);
698 	if (guts.ximages)            hash_destroy( guts.ximages, false);
699 	if (guts.menu_windows)       hash_destroy( guts.menu_windows, false);
700 	if (guts.windows)            hash_destroy( guts.windows, false);
701 	if (guts.clipboards)         hash_destroy( guts.clipboards, false);
702 	if (guts.clipboard_xfers)    hash_destroy( guts.clipboard_xfers, false);
703 	prima_cleanup_font_subsystem();
704 	bzero(&guts, sizeof(guts));
705 }
706 
707 static int
can_access_root_screen(void)708 can_access_root_screen(void)
709 {
710 	static int result = -1;
711 	XImage * im;
712 	XErrorEvent xr;
713 
714 	if ( result >= 0 ) return result;
715 	result = 0;
716 
717 	XFlush(DISP);
718 	prima_save_xerror_event(&xr);
719 	im = XGetImage( DISP, guts.root, 0, 0, 1, 1, AllPlanes, ZPixmap); /* XWayland fails here */
720 	prima_restore_xerror_event(NULL);
721 	if (im == NULL) goto EXIT;
722 
723 	XDestroyImage( im);
724 
725 #ifdef WITH_GTK_NONX11
726 	/* detect XQuartz */
727 	{
728 		char * display_str = getenv("DISPLAY");
729 		if ( display_str ) {
730 			struct stat s;
731 			if ((stat( display_str, &s) >= 0) && S_ISSOCK(s.st_mode))  /* is a socket */
732 				goto EXIT;
733 		}
734 	}
735 #endif
736 
737 	result = 1;
738 EXIT:
739 	return result;
740 }
741 
742 Bool
apc_application_begin_paint(Handle self)743 apc_application_begin_paint( Handle self)
744 {
745 	DEFXX;
746 	if ( guts. appLock > 0) return false;
747 	if ( !can_access_root_screen()) return false;
748 	prima_prepare_drawable_for_painting( self, false);
749 	XX-> flags. force_flush = 1;
750 	return true;
751 }
752 
753 Bool
apc_application_begin_paint_info(Handle self)754 apc_application_begin_paint_info( Handle self)
755 {
756 	prima_prepare_drawable_for_painting( self, false);
757 	return true;
758 }
759 
760 Bool
apc_application_create(Handle self)761 apc_application_create( Handle self)
762 {
763 	XSetWindowAttributes attrs;
764 	DEFXX;
765 	if ( !DISP) {
766 		Mdebug("apc_application_create: failed, x11 layer is not up\n");
767 		return false;
768 	}
769 
770 	XX-> type.application = true;
771 	XX-> type.widget = true;
772 	XX-> type.drawable = true;
773 
774 	attrs. event_mask = StructureNotifyMask | PropertyChangeMask;
775 	XX-> client = X_WINDOW = XCreateWindow( DISP, guts. root,
776 		0, 0, 1, 1, 0, CopyFromParent,
777 		InputOutput, CopyFromParent,
778 		CWEventMask, &attrs);
779 	XCHECKPOINT;
780 	if (!X_WINDOW) return false;
781 	hash_store( guts.windows, &X_WINDOW, sizeof(X_WINDOW), (void*)self);
782 
783 	XX-> pointer_id = crArrow;
784 	XX-> gdrawable = XX-> udrawable = guts. root;
785 	XX-> parent = None;
786 	XX-> origin. x = 0;
787 	XX-> origin. y = 0;
788 	XX-> ackSize = XX-> size = apc_application_get_size( self);
789 	XX-> owner = NULL_HANDLE;
790 	XX-> visual = &guts. visual;
791 
792 	XX-> flags. clip_owner = 1;
793 	XX-> flags. sync_paint = 0;
794 
795 	apc_component_fullname_changed_notify( self);
796 	guts. mouse_wheel_down = unix_rm_get_int( self, guts.qWheeldown, guts.qwheeldown, 5);
797 	guts. mouse_wheel_up = unix_rm_get_int( self, guts.qWheelup, guts.qwheelup, 4);
798 	guts. click_time_frame = unix_rm_get_int( self, guts.qClicktimeframe, guts.qclicktimeframe, guts. click_time_frame);
799 	guts. double_click_time_frame = unix_rm_get_int( self, guts.qDoubleclicktimeframe, guts.qdoubleclicktimeframe, guts. double_click_time_frame);
800 	guts. visible_timeout = unix_rm_get_int( self, guts.qBlinkvisibletime, guts.qblinkvisibletime, guts. visible_timeout);
801 	guts. invisible_timeout = unix_rm_get_int( self, guts.qBlinkinvisibletime, guts.qblinkinvisibletime, guts. invisible_timeout);
802 	guts. menu_timeout = unix_rm_get_int( self, guts.qSubmenudelay, guts.qsubmenudelay, guts. menu_timeout);
803 	guts. scroll_first = unix_rm_get_int( self, guts.qScrollfirst, guts.qscrollfirst, guts. scroll_first);
804 	guts. scroll_next = unix_rm_get_int( self, guts.qScrollnext, guts.qscrollnext, guts. scroll_next);
805 
806 	prima_send_create_event( X_WINDOW);
807 	return true;
808 }
809 
810 Bool
apc_application_close(Handle self)811 apc_application_close( Handle self)
812 {
813 	guts. applicationClose = true;
814 	return true;
815 }
816 
817 Bool
apc_application_destroy(Handle self)818 apc_application_destroy( Handle self)
819 {
820 	if ( X_WINDOW) {
821 		XDestroyWindow( DISP, X_WINDOW);
822 		XCHECKPOINT;
823 		hash_delete( guts.windows, (void*)&X_WINDOW, sizeof(X_WINDOW), false);
824 	}
825 	application = NULL_HANDLE;
826 	return true;
827 }
828 
829 Bool
apc_application_end_paint(Handle self)830 apc_application_end_paint( Handle self)
831 {
832 	DEFXX;
833 	XX-> flags. force_flush = 0;
834 	prima_cleanup_drawable_after_painting( self);
835 	return true;
836 }
837 
838 Bool
apc_application_end_paint_info(Handle self)839 apc_application_end_paint_info( Handle self)
840 {
841 	prima_cleanup_drawable_after_painting( self);
842 	return true;
843 }
844 
845 int
apc_application_get_gui_info(char * description,int len1,char * language,int len2)846 apc_application_get_gui_info( char * description, int len1, char * language, int len2)
847 {
848 	int ret = guiXLib;
849 	if ( description)
850 		strncpy( description, "X Window System", len1);
851 
852 #ifdef WITH_GTK
853 	if ( guts. use_gtk ) {
854 		if ( description) {
855 			strncat( description, " + GTK", len1);
856 #ifdef WITH_GTK_NONX11
857 			strncat( description, " with native support", len1);
858 #endif
859 #ifdef WITH_COCOA
860 			if ( guts. use_quartz)
861 				strncat( description, " + Cocoa", len1);
862 #endif
863 		}
864 		ret = guiGTK;
865 	}
866 #endif
867 
868 	if ( description)
869 		description[len1-1] = 0;
870 
871 	if ( language ) {
872 		char * lang = getenv("LANG");
873 		if ( lang ) {
874 			while (len2 > 1 && (*lang == '-' || islower((int)*lang))) {
875 				*(language++) = *(lang++);
876 			}
877 			*language = 0;
878 		} else
879 			*language = 0;
880 	}
881 
882 	return ret;
883 }
884 
885 Handle
apc_application_get_widget_from_point(Handle self,Point p)886 apc_application_get_widget_from_point( Handle self, Point p)
887 {
888 	XWindow from, to, child;
889 
890 	from = to = guts. root;
891 	p. y = DisplayHeight( DISP, SCREEN) - p. y - 1;
892 	while (XTranslateCoordinates(DISP, from, to, p.x, p.y, &p.x, &p.y, &child)) {
893 		if (child) {
894 			from = to;
895 			to = child;
896 		} else {
897 			Handle h;
898 			if ( to == from) to = X_WINDOW;
899 			h = (Handle)hash_fetch( guts.windows, (void*)&to, sizeof(to));
900 			return ( h == application) ? NULL_HANDLE : h;
901 		}
902 	}
903 	return NULL_HANDLE;
904 }
905 
906 Handle
apc_application_get_handle(Handle self,ApiHandle apiHandle)907 apc_application_get_handle( Handle self, ApiHandle apiHandle)
908 {
909 	return prima_xw2h(( XWindow) apiHandle);
910 }
911 
912 static Bool
wm_net_get_current_workarea(Rect * r)913 wm_net_get_current_workarea( Rect * r)
914 {
915 	Bool ret = false;
916 	unsigned long n;
917 	unsigned long *desktop = NULL, *workarea = NULL, *w;
918 
919 	if ( guts. icccm_only) return false;
920 
921 	desktop = ( unsigned long *) prima_get_window_property( guts. root,
922 					NET_CURRENT_DESKTOP, XA_CARDINAL,
923 					NULL, NULL,
924 					&n);
925 	if ( desktop == NULL || n < 1) goto EXIT;
926 	Mdebug("wm: current desktop = %d\n", *desktop);
927 
928 	workarea = ( unsigned long *) prima_get_window_property( guts. root,
929 					NET_WORKAREA, XA_CARDINAL,
930 					NULL, NULL,
931 					&n);
932 	if ( desktop == NULL || n < 1 || n <= *desktop ) goto EXIT;
933 
934 	w = workarea + *desktop * 4; /* XYWH quartets */
935 	r-> left   = w[0];
936 	r-> top    = w[1];
937 	r-> right  = w[2];
938 	r-> bottom = w[3];
939 	ret = true;
940 	Mdebug("wm: current workarea = %d:%d:%d:%d\n", w[0], w[1], w[2], w[3]);
941 
942 EXIT:
943 	free( workarea);
944 	free( desktop);
945 	return ret;
946 }
947 
948 Rect
apc_application_get_indents(Handle self)949 apc_application_get_indents( Handle self)
950 {
951 	Point sz;
952 	Rect  r;
953 
954 	bzero( &r, sizeof( r));
955 	if ( do_icccm_only) return r;
956 
957 	sz = apc_application_get_size( self);
958 	if ( wm_net_get_current_workarea( &r)) {
959 		r. right  = sz. x - r.right   - r. left;
960 		r. bottom = sz. y - r. bottom - r. top;
961 		if ( r. left   < 0) r. left   = 0;
962 		if ( r. top    < 0) r. top    = 0;
963 		if ( r. right  < 0) r. right  = 0;
964 		if ( r. bottom < 0) r. bottom = 0;
965 	}
966 
967 	return r;
968 }
969 
970 int
apc_application_get_os_info(char * system,int slen,char * release,int rlen,char * vendor,int vlen,char * arch,int alen)971 apc_application_get_os_info( char *system, int slen,
972 			char *release, int rlen,
973 			char *vendor, int vlen,
974 			char *arch, int alen)
975 {
976 	static struct utsname name;
977 	static Bool fetched = false;
978 
979 	if (!fetched) {
980 		if ( uname(&name)!=0) {
981 			strncpy( name. sysname, "Some UNIX", sizeof(name.sysname));
982 			name. sysname[ sizeof(name.sysname)-1] = 0;
983 			strncpy( name. release, "Unknown version of UNIX", sizeof(name.release));
984 			name. release[ sizeof(name.release)-1] = 0;
985 			strncpy( name. machine, "Unknown architecture", sizeof(name.machine));
986 			name. machine[ sizeof(name.machine)-1] = 0;
987 		}
988 		fetched = true;
989 	}
990 
991 	if (system) {
992 		strncpy( system, name. sysname, slen);
993 		system[ slen-1] = 0;
994 	}
995 	if (release) {
996 		strncpy( release, name. release, rlen);
997 		release[ rlen-1] = 0;
998 	}
999 	if (vendor) {
1000 		strncpy( vendor, "Unknown vendor", vlen);
1001 		vendor[ vlen-1] = 0;
1002 	}
1003 	if (arch) {
1004 		strncpy( arch, name. machine, alen);
1005 		arch[ alen-1] = 0;
1006 	}
1007 
1008 	return apcUnix;
1009 }
1010 
1011 Point
apc_application_get_size(Handle self)1012 apc_application_get_size( Handle self)
1013 {
1014 	return guts. displaySize;
1015 }
1016 
1017 Box *
apc_application_get_monitor_rects(Handle self,int * nrects)1018 apc_application_get_monitor_rects( Handle self, int * nrects)
1019 {
1020 #if defined(HAVE_X11_EXTENSIONS_XRANDR_H) && (RANDR_MAJOR > 1 || (RANDR_MAJOR == 1 && RANDR_MINOR > 3))
1021 	XRRScreenResources * sr;
1022 	Box * ret = NULL;
1023 
1024 	if ( !guts. randr_extension) {
1025 		*nrects = 0;
1026 		return NULL;
1027 	}
1028 
1029 	XCHECKPOINT;
1030 	sr = XRRGetScreenResourcesCurrent(DISP,guts.root);
1031 	if ( sr ) {
1032 		int i;
1033 		ret = malloc(sizeof(Box) * sr->ncrtc);
1034 		*nrects = sr->ncrtc;
1035 		for ( i = 0; i < sr->ncrtc; i++) {
1036 			XRRCrtcInfo * ci = XRRGetCrtcInfo (DISP, sr, sr->crtcs[i]);
1037 			ret[i].x      = ci->x;
1038 			ret[i].y      = guts.displaySize.y - ci->height - ci->y;
1039 			ret[i].width  = ci->width;
1040 			ret[i].height = ci->height;
1041 			XRRFreeCrtcInfo(ci);
1042 		}
1043 		XRRFreeScreenResources(sr);
1044 		XCHECKPOINT;
1045 	} else {
1046 		*nrects = 0;
1047 	}
1048 	return ret;
1049 #else
1050 	*nrects = 0;
1051 	return NULL;
1052 #endif
1053 }
1054 
1055 Bool
apc_application_go(Handle self)1056 apc_application_go( Handle self)
1057 {
1058 	if (!application) return false;
1059 
1060 	XNoOp( DISP);
1061 	XFlush( DISP);
1062 	guts. application_stop_signal = false;
1063 	while ( !guts. application_stop_signal && prima_one_loop_round( WAIT_ALWAYS, true))
1064 		;
1065 	guts. application_stop_signal = false;
1066 	return true;
1067 }
1068 
1069 Bool
apc_application_lock(Handle self)1070 apc_application_lock( Handle self)
1071 {
1072 	guts. appLock++;
1073 	return true;
1074 }
1075 
1076 Bool
apc_application_unlock(Handle self)1077 apc_application_unlock( Handle self)
1078 {
1079 	if ( guts. appLock > 0) guts. appLock--;
1080 	return true;
1081 }
1082 
1083 Bool
apc_application_stop(Handle self)1084 apc_application_stop( Handle self)
1085 {
1086 	if ( application == NULL_HANDLE ) return false;
1087 	guts. application_stop_signal = true;
1088 	return true;
1089 }
1090 
1091 Bool
apc_application_sync(void)1092 apc_application_sync(void)
1093 {
1094 	XSync( DISP, false);
1095 	return true;
1096 }
1097 
1098 Bool
apc_application_yield(Bool wait_for_event)1099 apc_application_yield( Bool wait_for_event)
1100 {
1101 	if (!application) return false;
1102 	guts. application_stop_signal = false;
1103 	prima_one_loop_round(wait_for_event ? WAIT_IF_NONE : WAIT_NEVER, true);
1104 	guts. application_stop_signal = false;
1105 	XSync( DISP, false);
1106 	return application != NULL_HANDLE && !guts. applicationClose;
1107 }
1108