1 #include "CtrlCore.h"
2 
3 #ifdef GUI_X11
4 
5 #include <locale.h>
6 
7 namespace Upp {
8 
9 #define LLOG(x)  // DLOG(x)
10 
11 XIM Ctrl::xim;
12 
XAtomRaw(const char * name)13 Atom XAtomRaw(const char *name)
14 {
15 	return XInternAtom(Xdisplay, name, XFalse);
16 }
17 
XAtom(const char * name)18 Atom XAtom(const char *name)
19 {
20 	GuiLock __;
21 	Atom x;
22 	INTERLOCKED {
23 		static VectorMap<String, int> atoms;
24 		int q = atoms.Get(name, Null);
25 		if(IsNull(q)) {
26 			q = XAtomRaw(name);
27 			atoms.Add(name, q);
28 		}
29 		x = q;
30 	}
31 	return x;
32 }
33 
XAtomName(Atom atom)34 String XAtomName(Atom atom)
35 {
36 	GuiLock __;
37 	LLOG("GetAtomName");
38 	return XGetAtomName(Xdisplay, atom);
39 }
40 
GetProperty(Window w,Atom property,Atom rtype)41 String GetProperty(Window w, Atom property, Atom rtype)
42 {
43 	GuiLock __;
44 	LLOG("GetProperty");
45 	String result;
46 	int format;
47 	unsigned long nitems, after = 1;
48 	long offset = 0;
49 	Atom type = None;
50 	unsigned char *data;
51 	long rsize = minmax((long)(XMaxRequestSize(Xdisplay) - 100), (long)256, (long)65536);
52 	while(after > 0) {
53 		if(XGetWindowProperty(Xdisplay, w, property, offset, rsize, XFalse,
54 	                          rtype, &type, &format, &nitems, &after, &data) != Success)
55 			break;
56 	    if(type == None)
57 			break;
58 		if(data) {
59 			int len = format == 32 ? sizeof(unsigned long) * nitems : nitems * (format >> 3);
60 			result.Cat(data, len);
61 			XFree((char *)data);
62 			offset += nitems / (32 / format);
63 		}
64 		else
65 			break;
66 	}
67 	result.Shrink();
68 	XFlush(Xdisplay);
69 	return result;
70 }
71 
WaitForEvent(Window w,int type,XEvent & event)72 bool WaitForEvent(Window w, int type, XEvent& event){
73 	GuiLock __;
74 	for(int i = 0; i < 80; i++) {
75 		if(XCheckTypedWindowEvent(Xdisplay, w, type, &event))
76 			return true;
77 		XFlush(Xdisplay);
78 		Sleep(50);
79 	}
80 	LOG("WaitForEvent failed");
81 	return false;
82 }
83 
84 
ReadPropertyData(Window w,Atom property,Atom rtype)85 String ReadPropertyData(Window w, Atom property, Atom rtype)
86 {
87 	GuiLock __;
88 	static Atom XA_INCR = XAtom("INCR");
89 	Atom type;
90 	int format;
91 	unsigned long nitems, after;
92 	unsigned char *ptr;
93 	String r;
94 	if(XGetWindowProperty(Xdisplay, w, property, 0, 0, XFalse, AnyPropertyType,
95 	                      &type, &format, &nitems, &after, &ptr) == Success && type != None) {
96 		XFree(ptr);
97 		if(type == XA_INCR) {
98 			XDeleteProperty(Xdisplay, w, property);
99 			XEvent event;
100 			for(;;) {
101 				XFlush(Xdisplay);
102 				if(!WaitForEvent(w, PropertyNotify, event))
103 					break;
104 				if(event.xproperty.atom == property && event.xproperty.state == PropertyNewValue) {
105 					String x = GetProperty(w, property);
106 					if(!x.GetLength())
107 						break;
108 					r.Cat(x);
109 					XDeleteProperty(Xdisplay, w, property);
110 				}
111 			}
112 		}
113 		else {
114 			r = GetProperty(w, property);
115 			XDeleteProperty(Xdisplay, w, property);
116 		}
117 	}
118 	return r;
119 }
120 
GetPropertyInts(Window w,Atom property,Atom rtype)121 Vector<int> GetPropertyInts(Window w, Atom property, Atom rtype)
122 {
123 	GuiLock __;
124 	Vector<int> result;
125 	String p = GetProperty(w, property, rtype);
126 	const long int *ptr = (const long int *)~p;
127 	const long int *lim = ptr + p.GetLength() / sizeof(long int);
128 	result.Reserve(p.GetLength() / sizeof(long int));
129 	while(ptr < lim)
130 		result.Add(*ptr++);
131 	return result;
132 }
133 
_NET_Supported()134 Index<Atom>& _NET_Supported()
135 {
136 	static Index<Atom> q;
137 	return q;
138 }
139 
140 bool X11ErrorTrap;
141 
TrapX11Errors()142 bool Ctrl::TrapX11Errors()
143 {
144 	GuiLock __;
145 	bool b = X11ErrorTrap;
146 	X11ErrorTrap = true;
147 	return b;
148 }
149 
UntrapX11Errors(bool b)150 void Ctrl::UntrapX11Errors(bool b)
151 {
152 	GuiLock __;
153 	X11ErrorTrap = b;
154 }
155 
sPanicMessageBox(const char * title,const char * text)156 void sPanicMessageBox(const char *title, const char *text)
157 {
158 	IGNORE_RESULT(
159 		write(2, text, strlen(text))
160 	);
161 	IGNORE_RESULT(
162 		write(2, "\n", 1)
163 	);
164 	if(Ctrl::grabWindow) {
165 		LLOG("RELEASE GRAB");
166 		XUngrabPointer(Xdisplay, CurrentTime);
167 		XFlush(Xdisplay);
168 	}
169 	XDisplay *display = XOpenDisplay(NULL);
170 	if(!display)
171 		return;
172 	int screen = DefaultScreen(display);
173 	int x = (DisplayWidth(display, screen) - 600) / 2;
174 	int y = (DisplayHeight(display, screen) - 120) / 2;
175 	Window win = XCreateSimpleWindow(display, RootWindow(display, screen),
176 	                                 x, y, 600, 120, 4,
177 	                                 BlackPixel(display, screen),
178 	                                 WhitePixel(display, screen));
179 	XSizeHints size_hints;
180 	size_hints.flags = PPosition|PSize|PMinSize;
181 	size_hints.x = x;
182 	size_hints.y = x;
183 	size_hints.width = 600;
184 	size_hints.height = 120;
185 	size_hints.min_width = 600;
186 	size_hints.min_height = 120;
187 	char *h[1];
188 	char hh[1];
189 	*hh = 0;
190 	h[0] = hh;
191 	XSetStandardProperties(display, win, title, title, None, h, 0, &size_hints);
192 	XSelectInput(display, win, ExposureMask|KeyPressMask|ButtonPressMask|StructureNotifyMask);
193 	XGCValues values;
194 	GC gc = XCreateGC(display, win, 0, &values);
195 	unsigned long wina[1];
196 	wina[0] = XInternAtom(display, "_NET_WM_STATE_ABOVE", XFalse);
197 	XChangeProperty(display, win,
198 	                XInternAtom(display, "_NET_WM_STATE", XFalse),
199 	                XInternAtom(display, "ATOM", XFalse), 32,
200 	                PropModeReplace, (const unsigned char *)&wina, 1);
201 	XMapWindow(display, win);
202 //	XSetInputFocus(display, win, RevertToParent, CurrentTime);
203 	XRaiseWindow(display, win);
204 	XFontStruct *font_info = XQueryFont(display, XGContextFromGC(gc));
205 	for(;;) {
206 		XEvent e;
207 		XNextEvent(display, &e);
208 		switch(e.type) {
209 		case KeyPress:
210 		case ButtonPress:
211 			XFreeFont(display, font_info);
212 			XFreeGC(display, gc);
213 			XCloseDisplay(display);
214 		#ifdef _DEBUG
215 			__BREAK__;
216 		#endif
217 			return;
218 		case Expose:
219 			int y = 20;
220 			const char *b = text;
221 			for(;;) {
222 				const char *e = strchr(b, '\n');
223 				if(!e) break;
224 				XDrawString(display, win, gc, 20, y, b, e - b);
225 				y += font_info->max_bounds.ascent + font_info->max_bounds.descent;
226 				b = e + 1;
227 			}
228 			XDrawString(display, win, gc, 20, y, b, strlen(b));
229 			break;
230 		}
231 	}
232 }
233 
234 #ifdef _DEBUG
235 #define INI_PREFIX "DEBUG_"
236 #else
237 #define INI_PREFIX
238 #endif
239 
X11ErrorHandler(XDisplay *,XErrorEvent * error)240 int X11ErrorHandler(XDisplay *, XErrorEvent *error)
241 {
242 	if(X11ErrorTrap || IsPanicMode()) return 0;
243 
244 	if(GetIniKey(INI_PREFIX "X11_ERRORS") != "1")
245 		return 0;
246 
247 	static const char *request[] = {
248 		"",
249 		"X_CreateWindow",
250 		"X_ChangeWindowAttributes",
251 		"X_GetWindowAttributes",
252 		"X_DestroyWindow",
253 		"X_DestroySubwindows",
254 		"X_ChangeSaveSet",
255 		"X_ReparentWindow",
256 		"X_MapWindow",
257 		"X_MapSubwindows",
258 		"X_UnmapWindow",
259 		"X_UnmapSubwindows",
260 		"X_ConfigureWindow",
261 		"X_CirculateWindow",
262 		"X_GetGeometry",
263 		"X_QueryTree",
264 		"X_InternAtom",
265 		"X_GetAtomName",
266 		"X_ChangeProperty",
267 		"X_DeleteProperty",
268 		"X_GetProperty",
269 		"X_ListProperties",
270 		"X_SetSelectionOwner",
271 		"X_GetSelectionOwner",
272 		"X_ConvertSelection",
273 		"X_SendEvent",
274 		"X_GrabPointer",
275 		"X_UngrabPointer",
276 		"X_GrabButton",
277 		"X_UngrabButton",
278 		"X_ChangeActivePointerGrab",
279 		"X_GrabKeyboard",
280 		"X_UngrabKeyboard",
281 		"X_GrabKey",
282 		"X_UngrabKey",
283 		"X_AllowEvents",
284 		"X_GrabServer",
285 		"X_UngrabServer",
286 		"X_QueryPointer",
287 		"X_GetMotionEvents",
288 		"X_TranslateCoords",
289 		"X_WarpPointer",
290 		"X_SetInputFocus",
291 		"X_GetInputFocus",
292 		"X_QueryKeymap",
293 		"X_OpenFont",
294 		"X_CloseFont",
295 		"X_QueryFont",
296 		"X_QueryTextExtents",
297 		"X_ListFonts",
298 		"X_ListFontsWithInfo",
299 		"X_SetFontPath",
300 		"X_GetFontPath",
301 		"X_CreatePixmap",
302 		"X_FreePixmap",
303 		"X_CreateGC",
304 		"X_ChangeGC",
305 		"X_CopyGC",
306 		"X_SetDashes",
307 		"X_SetClipRectangles",
308 		"X_FreeGC",
309 		"X_ClearArea",
310 		"X_CopyArea",
311 		"X_CopyPlane",
312 		"X_PolyPoint",
313 		"X_PolyLine",
314 		"X_PolySegment",
315 		"X_PolyRectangle",
316 		"X_PolyArc",
317 		"X_FillPoly",
318 		"X_PolyFillRectangle",
319 		"X_PolyFillArc",
320 		"X_PutImage",
321 		"X_GetImage",
322 		"X_PolyText8",
323 		"X_PolyText16",
324 		"X_ImageText8",
325 		"X_ImageText16",
326 		"X_CreateColormap",
327 		"X_FreeColormap",
328 		"X_CopyColormapAndFree",
329 		"X_InstallColormap",
330 		"X_UninstallColormap",
331 		"X_ListInstalledColormaps",
332 		"X_AllocColor",
333 		"X_AllocNamedColor",
334 		"X_AllocColorCells",
335 		"X_AllocColorPlanes",
336 		"X_FreeColors",
337 		"X_StoreColors",
338 		"X_StoreNamedColor",
339 		"X_QueryColors",
340 		"X_LookupColor",
341 		"X_CreateCursor",
342 		"X_CreateGlyphCursor",
343 		"X_FreeCursor",
344 		"X_RecolorCursor",
345 		"X_QueryBestSize",
346 		"X_QueryExtension",
347 		"X_ListExtensions",
348 		"X_ChangeKeyboardMapping",
349 		"X_GetKeyboardMapping",
350 		"X_ChangeKeyboardControl",
351 		"X_GetKeyboardControl",
352 		"X_Bell",
353 		"X_ChangePointerControl",
354 		"X_GetPointerControl",
355 		"X_SetScreenSaver",
356 		"X_GetScreenSaver",
357 		"X_ChangeHosts",
358 		"X_ListHosts",
359 		"X_SetAccessControl",
360 		"X_SetCloseDownMode",
361 		"X_KillClient",
362 		"X_RotateProperties",
363 		"X_ForceScreenSaver",
364 		"X_SetPointerMapping",
365 		"X_GetPointerMapping",
366 		"X_SetModifierMapping",
367 		"X_GetModifierMapping",
368 		"X_NoOperation",
369 	};
370 
371 	char h[512];
372 	XGetErrorText(Xdisplay, error->error_code, h, 512);
373 	String e;
374 	e << "X Error: " << h;
375 	if(error->request_code < __countof(request))
376 		e << "\nrequest: " << request[error->request_code];
377 	e << "\nresource id: " << (int)error->resourceid << " = " << Format("%0X", (int)error->resourceid);
378 
379 	RLOG(e);
380 	puts(e);
381 
382 	Panic(e);
383 
384 	return 0;
385 }
386 
SetX11ErrorHandler()387 void SetX11ErrorHandler()
388 {
389 	XSetErrorHandler(X11ErrorHandler);
390 }
391 
392 INITBLOCK {
393 	InstallPanicMessageBox(sPanicMessageBox);
394 }
395 
396 void Ctrl::InstallPanicBox()
397 {
398 	InstallPanicMessageBox(sPanicMessageBox);
399 }
400 
InitX11(const char * display)401 void Ctrl::InitX11(const char *display)
402 {
403 	GuiLock __;
404 
405 	XInitThreads();
406 
407 	InstallPanicMessageBox(sPanicMessageBox);
408 
409 	InitX11Draw(display);
410 	InitTimer();
411 	byte dummy[5];
412 	Xbuttons = XGetPointerMapping(Xdisplay, dummy, 5);
413 
414 	Xeventtime = CurrentTime;
415 	SetX11ErrorHandler();
416 	if(GetIniKey(INI_PREFIX "X11_SYNCHRONIZE") == "1")
417 		XSynchronize(Xdisplay, 1);
418 	Vector<int> nets = GetPropertyInts(Xroot, XAtom("_NET_SUPPORTED"));
419 	for(int i = 0; i < nets.GetCount(); i++)
420 		_NET_Supported().Add(nets[i]);
421 
422 	Font::SetDefaultFont(Arial(12));
423 
424 	ReSkin();
425 
426 	GUI_GlobalStyle_Write(GUISTYLE_XP);
427 	GUI_DragFullWindow_Write(1);
428 	GUI_PopUpEffect_Write(IsCompositedGui() ? GUIEFFECT_NONE : GUIEFFECT_SLIDE);
429 	GUI_DropShadows_Write(1);
430 	GUI_AltAccessKeys_Write(1);
431 	GUI_AKD_Conservative_Write(0);
432 
433 	setlocale(LC_ALL, "en_US.utf8");
434 	if(XSupportsLocale()) {
435 		XSetLocaleModifiers("");
436 		xim = XOpenIM(Xdisplay, NULL, NULL, NULL);
437 	}
438 	else {
439 		xim = NULL;
440 		LOG("IM unsupported!");
441 	}
442 
443 	Csizeinit();
444 }
445 
ExitX11()446 void Ctrl::ExitX11()
447 {
448 	GuiLock __;
449 //	if(xic)
450 //		XDestroyIC(xic);
451 	TopWindow::ShutdownWindows();
452 	CloseTopCtrls();
453 	for(int i = 0; i < hotkey.GetCount(); i++)
454 		UnregisterSystemHotKey(i);
455 	if(xim)
456 		XCloseIM(xim);
457 }
458 
FindScreensResolutions()459 Vector<Rect> FindScreensResolutions()
460 {
461 	Vector<Rect> screensResolutions;
462 	int event, error;
463 
464 	if(XineramaQueryExtension(Xdisplay, &event, &error)) {
465 		if(XineramaIsActive(Xdisplay)) {
466 			int screensNumber = 0;
467 			XineramaScreenInfo* info = XineramaQueryScreens(Xdisplay, &screensNumber);
468 			for(int i = 0; i < screensNumber; i++)
469 				screensResolutions.Add(Rect(info[i].x_org, info[i].y_org, info[i].x_org + info[i].width, info[i].y_org + info[i].height));
470 			XFree(info);
471 		}
472 	}
473 	return screensResolutions;
474 }
475 
FindScreensStruts()476 Vector<Rect> FindScreensStruts()
477 {
478 	Vector<Rect> struts;
479 
480 	Vector<int> clients = GetPropertyInts(Xroot, XAtom("_NET_CLIENT_LIST"));
481 	for (int i = 0; i < clients.GetCount(); i++) {
482 		Vector<int> strut = GetPropertyInts(clients[i], XAtom("_NET_WM_STRUT"));
483 		if(strut.GetCount() == 4)
484 			struts.Add(Rect(strut[0], strut[2], strut[1], strut[3]));
485 	}
486 	return struts;
487 }
488 
GetDefaultWindowRect()489 Rect Ctrl::GetDefaultWindowRect()
490 {
491 	GuiLock __;
492 	static int width  = 0;
493 	static int height = 0;
494 	static int left   = 0;
495 	static int top    = 0;
496 	if (width == 0 && height == 0) {
497 		Vector<Rect> screens = FindScreensResolutions();
498 		if(screens.GetCount()) {
499 			width  = screens[0].Width();
500 			height = screens[0].Height();
501 			left   = screens[0].left;
502 			top    = screens[0].top;
503 		}
504 		else {
505 			width  = Xwidth;
506 			height = Xheight;
507 		}
508 	}
509 
510 	static int pos = min(width / 10, 50);
511 	pos += 10;
512 	int cx = width * 2 / 3;
513 	int cy = height * 2 / 3;
514 	if(pos + cx + 50 > width || pos + cy + 50 > height)
515 		pos = 0;
516 	return RectC(left + pos + 20, top + pos + 20, cx, cy);
517 }
518 
GetWorkArea(Array<Rect> & out)519 void Ctrl::GetWorkArea(Array<Rect>& out)
520 {
521 	Vector<Rect> workAreas = FindScreensResolutions();
522 	Vector<Rect> struts    = FindScreensStruts();
523 	for (int i = 0; i < workAreas.GetCount(); i++) {
524 		if (i < struts.GetCount()) {
525 			workAreas[i].left   += struts[i].left;
526 			workAreas[i].right  -= struts[i].right;
527 			workAreas[i].top    += struts[i].top;
528 			workAreas[i].bottom -= struts[i].bottom;
529 		}
530 		out.Add(workAreas[i]);
531 	}
532 	if (out.IsEmpty())
533 		out.Add(GetPrimaryWorkArea());
534 }
535 
GetWorkArea() const536 Rect Ctrl::GetWorkArea() const
537 {
538 	GuiLock __;
539 
540 	static Array<Rect> rc;
541 	if (rc.IsEmpty())
542 		GetWorkArea(rc);
543 
544 	Point pt = GetScreenRect().TopLeft();
545 	for (int i = 0; i < rc.GetCount(); i++)
546 		if(rc[i].Contains(pt))
547 			return rc[i];
548 	return GetPrimaryWorkArea();
549 }
550 
GetVirtualWorkArea()551 Rect Ctrl::GetVirtualWorkArea()
552 {
553 	GuiLock __;
554 	static Rect r;
555 	if(r.right == 0) {
556 		Vector<int> x = GetPropertyInts(Xroot, XAtom("_NET_WORKAREA"));
557 		if(x.GetCount())
558 			r = RectC(x[0], x[1], x[2], x[3]);
559 		else
560 			r = RectC(0, 0, Xwidth, Xheight);
561 	}
562 	return r;
563 }
564 
GetVirtualScreenArea()565 Rect Ctrl::GetVirtualScreenArea()
566 {
567 	return RectC(0, 0, Xwidth, Xheight);
568 }
569 
GetPrimaryWorkArea()570 Rect Ctrl::GetPrimaryWorkArea()
571 {
572 	GuiLock __;
573 	static Rect r;
574 	if(r.right == 0) {
575 		Array<Rect> rc;
576 		GetWorkArea(rc);
577 		rc.GetCount() ? r = rc[0] : r = GetVirtualScreenArea();
578 	}
579 	return r;
580 }
581 
GetPrimaryScreenArea()582 Rect Ctrl::GetPrimaryScreenArea()
583 {
584 	GuiLock __;
585 	static Rect r;
586 	if(r.right == 0) {
587 		Vector<Rect> screens = FindScreensResolutions();
588 		screens.GetCount() ? r = screens[0] : r = GetVirtualScreenArea();
589 	}
590 	return r;
591 }
592 
GetKbdDelay()593 int Ctrl::GetKbdDelay()
594 {
595 	return 250;
596 }
597 
GetKbdSpeed()598 int Ctrl::GetKbdSpeed()
599 {
600 	return 25;
601 }
602 
603 
604 #ifdef _DEBUG
605 extern bool __X11_Grabbing;
_DBG_Ungrab(void)606 void _DBG_Ungrab(void) {
607 	if(__X11_Grabbing)
608 	{
609 		XUngrabPointer(Xdisplay, CurrentTime);
610 		XUngrabKeyboard(Xdisplay, CurrentTime);
611 		XFlush(Xdisplay);
612 		__X11_Grabbing = false;
613 	}
614 }
615 #endif
616 
617 }
618 
619 #endif
620