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