1 #include "CtrlCore.h"
2
3 #ifdef GUI_X11
4
5 #include <X11/Xlocale.h>
6
7 namespace Upp {
8
9 #ifdef _DEBUG
10
11 bool Ctrl::LogMessages
12
13 // = true _DBG_
14 ;
15 bool __X11_Grabbing = false;
16 #endif
17
18 #define LLOG(x) // DLOG(x)
19 #define LTIMING(x) // RTIMING(x)
20 #define LDUMP(x) // DDUMP(x)
21 #define LDUMPC(x) // DDUMPC(x)
22
23 // #define SYNCHRONIZE
24
25 #define x_Event(x) { x, #x },
26
27 static
28 struct XEventMap {
29 int ID;
30 const char *name;
31 }
32 sXevent[] = {
33 #include "X11Event.i"
34 { 0, NULL }
35 };
36
Xwindow()37 ArrayMap<Window, Ctrl::XWindow>& Ctrl::Xwindow()
38 {
39 return Single< ArrayMap<Window, XWindow> >();
40 }
41
42 int Ctrl::WndCaretTime;
43 bool Ctrl::WndCaretVisible;
44 int Ctrl::Xbuttons;
45 Window Ctrl::grabWindow, Ctrl::focusWindow;
46 int Ctrl::Xeventtime;
47
48 int Ctrl::PopupGrab;
49 Ptr<Ctrl> Ctrl::popupWnd;
50
51 Point Ctrl::mousePos;
52
53 static int s_starttime;
54
DoPaint(const Vector<Rect> & invalid)55 void Ctrl::DoPaint(const Vector<Rect>& invalid)
56 {
57 GuiLock __;
58 if(IsVisible()) {
59 LTIMING("DoPaint");
60 fullrefresh = false;
61 // if(GLX) return;
62 GC gc = XCreateGC(Xdisplay, (Drawable)top->window, 0, 0);
63 XftDraw *xftdraw = XftDrawCreate(Xdisplay, (Drawable) top->window,
64 DefaultVisual(Xdisplay, Xscreenno), Xcolormap);
65 SystemDraw draw(top->window, gc, xftdraw, invalid);
66 painting = true;
67 UpdateArea(draw, draw.GetClip());
68 painting = false;
69 XftDrawDestroy(xftdraw);
70 XFreeGC(Xdisplay, gc);
71 }
72 }
73
WndScrollView(const Rect & r,int dx,int dy)74 void Ctrl::WndScrollView(const Rect& r, int dx, int dy)
75 {
76 GuiLock __;
77 if(r.IsEmpty() || !GetWindow()) return;
78 int cx = r.Width() - abs(dx);
79 int cy = r.Height() - abs(dy);
80 GC gc = XCreateGC(Xdisplay, GetWindow(), 0, 0);
81 XCopyArea(Xdisplay, GetWindow(), GetWindow(), gc,
82 r.left + max(-dx, 0), r.top + max(-dy, 0), cx, cy,
83 r.left + max(dx, 0), r.top + max(dy, 0));
84 XFreeGC(Xdisplay, gc);
85 XWindow *xw = GetXWindow();
86 Vector<Rect> ur;
87 if(xw)
88 for(int i = 0; i < xw->invalid.GetCount(); i++)
89 if(xw->invalid[i].Intersects(r))
90 ur.Add(xw->invalid[i]);
91 for(int i = 0; i < ur.GetCount(); i++)
92 Invalidate(*xw, ur[i].Offseted(dx, dy));
93 }
94
IsWaitingEvent()95 bool Ctrl::IsWaitingEvent()
96 {
97 ASSERT_(IsMainThread(), "IsWaitingEvent can only run in the main thread");
98 GuiLock __;
99 return XPending(Xdisplay);
100 }
101
CtrlFromWindow(Window w)102 Ctrl *Ctrl::CtrlFromWindow(Window w)
103 {
104 GuiLock __;
105 int q = Xwindow().Find(w);
106 return q >= 0 ? Xwindow()[q].ctrl : NULL;
107 }
108
GetXWindow()109 Ctrl::XWindow *Ctrl::GetXWindow()
110 {
111 GuiLock __;
112 if(!top) return NULL;
113 int q = Xwindow().Find(top->window);
114 return q >= 0 ? &Xwindow()[q] : NULL;
115 }
116 // 01/12/2007 - mdelfede
117 // added support for windowed controls
118
119 // Gets handle of window containing control
GetParentWindow(void) const120 Window Ctrl::GetParentWindow(void) const
121 {
122 GuiLock __;
123 Ctrl const *q = GetParentWindowCtrl();
124 if(q)
125 return q->top->window;
126 else
127 return 0;
128
129 } // END Ctrl::GetParentWindow()
130
131 // Get control with parentwindow as handle
GetParentWindowCtrl(void) const132 Ctrl *Ctrl::GetParentWindowCtrl(void) const
133 {
134 GuiLock __;
135 Ctrl *q = parent;
136 while(q && !q->top)
137 q = q->parent;
138 return q;
139
140 } // END Ctrl::GetParentWindowCtrl()
141
142 // Gets the rect inside the parent window
GetRectInParentWindow(void) const143 Rect Ctrl::GetRectInParentWindow(void) const
144 {
145 GuiLock __;
146 Rect r = GetScreenRect();
147 Ctrl *q = parent;
148 while(q && !q->top)
149 q = q->parent;
150 if(q)
151 {
152 Rect pr = q->GetScreenRect();
153 r -= pr.TopLeft();
154 }
155 return r;
156 }
157 // 01/12/2007 - END
158
HookProc(XEvent * event)159 bool Ctrl::HookProc(XEvent *event) { return false; }
160
161 void DnDRequest(XSelectionRequestEvent *se);
162 void DnDClear();
163
X11mods(dword key)164 dword X11mods(dword key)
165 {
166 dword mod = 0;
167 if(key & K_ALT)
168 mod |= Mod1Mask;
169 if(key & K_SHIFT)
170 mod |= ShiftMask;
171 if(key & K_CTRL)
172 mod |= ControlMask;
173 return mod;
174 }
175
176 Vector<Event<> > Ctrl::hotkey;
177 Vector<dword> Ctrl::keyhot;
178 Vector<dword> Ctrl::modhot;
179
RegisterSystemHotKey(dword key,Function<void ()> cb)180 int Ctrl::RegisterSystemHotKey(dword key, Function<void ()> cb)
181 {
182 GuiLock __;
183 ASSERT(key >= K_DELTA);
184 bool b = TrapX11Errors();
185 KeyCode k = XKeysymToKeycode(Xdisplay, key & 0xffff);
186 dword mod = X11mods(key);
187 bool r = false;
188 for(dword nlock = 0; nlock < 2; nlock++)
189 for(dword clock = 0; clock < 2; clock++)
190 for(dword slock = 0; slock < 2; slock++)
191 r = XGrabKey(Xdisplay, k,
192 mod | (nlock * Mod2Mask) | (clock * LockMask) | (slock * Mod3Mask),
193 Xroot, true, GrabModeAsync, GrabModeAsync) || r;
194 UntrapX11Errors(b);
195 if(!r) return -1;
196 int q = hotkey.GetCount();
197 for(int i = 0; i < hotkey.GetCount(); i++)
198 if(!hotkey[i]) {
199 q = i;
200 break;
201 }
202 hotkey.At(q) << cb;
203 keyhot.At(q) = k;
204 modhot.At(q) = mod;
205 return q;
206 }
207
UnregisterSystemHotKey(int id)208 void Ctrl::UnregisterSystemHotKey(int id)
209 {
210 GuiLock __;
211 if(id >= 0 && id < hotkey.GetCount() && hotkey[id]) {
212 bool b = TrapX11Errors();
213 for(dword nlock = 0; nlock < 2; nlock++)
214 for(dword clock = 0; clock < 2; clock++)
215 for(dword slock = 0; slock < 2; slock++)
216 XUngrabKey(Xdisplay, keyhot[id],
217 modhot[id] | (nlock * Mod2Mask) | (clock * LockMask) | (slock * Mod3Mask),
218 Xroot);
219 UntrapX11Errors(b);
220 hotkey[id].Clear();
221 }
222 }
223
ProcessEvent(XEvent * event)224 void Ctrl::ProcessEvent(XEvent *event)
225 {
226 GuiLock __;
227 if(xim && XFilterEvent(event, None))
228 return;
229 if(event->type == KeyPress)
230 for(int i = 0; i < hotkey.GetCount(); i++)
231 if(hotkey[i] && keyhot[i] == event->xkey.keycode
232 && modhot[i] == (event->xkey.state & (Mod1Mask|ShiftMask|ControlMask))) {
233 hotkey[i]();
234 return;
235 }
236 ArrayMap<Window, Ctrl::XWindow>& xmap = Xwindow();
237 for(int i = 0; i < xmap.GetCount(); i++)
238 if(xmap[i].ctrl && xmap[i].ctrl->HookProc(event))
239 return;
240 FocusSync();
241 if(event->type == PropertyNotify &&
242 (event->xproperty.atom == XAtom("_QT_SETTINGS_TIMESTAMP_") ||
243 event->xproperty.atom == XAtom("_KDE_NET_USER_TIME"))) {
244 LLOG("Property notify " << XAtomName(event->xproperty.atom)
245 << " " << (void *)event->xany.window);
246 for(int i = 0; i < Xwindow().GetCount(); i++) {
247 if(Xwindow().GetKey(i)) {
248 Ctrl *q = Xwindow()[i].ctrl;
249 if(q) q->Refresh();
250 }
251 }
252 }
253 if(event->type == SelectionRequest &&
254 event->xselectionrequest.owner == xclipboard().win) {
255 if(event->xselectionrequest.selection == XAtom("XdndSelection"))
256 DnDRequest(&event->xselectionrequest);
257 else
258 xclipboard().Request(&event->xselectionrequest);
259 return;
260 }
261 if(event->type == SelectionClear &&
262 event->xselectionclear.window == xclipboard().win) {
263 if(event->xselectionclear.selection == XAtom("PRIMARY")) {
264 sel_formats.Clear();
265 sel_ctrl = NULL;
266 }
267 if(event->xselectionclear.selection == XAtom("CLIPBOARD"))
268 xclipboard().Clear();
269 if(event->xselectionrequest.selection == XAtom("XdndSelection"))
270 DnDClear();
271 return;
272 }
273 int q = xmap.Find(event->xany.window);
274 #ifdef _DEBUG
275 bool eo = false;
276 if(LogMessages && event->type != NoExpose && event->type != PropertyNotify
277 && event->type != MotionNotify)
278 for(XEventMap *m = sXevent; m->ID; m++)
279 if(m->ID == event->type) {
280 if(!s_starttime)
281 s_starttime = msecs();
282 int t = msecs() - s_starttime;
283 VppLog() << Format("%d.%01d", t / 1000, t % 1000);
284 VppLog() << " EVENT " << Format("%-20.20s", m->name);
285 VppLog() << "[window: " << event->xany.window << "] ";
286 if(q >= 0)
287 VppLog() << '<' << UPP::Name(Xwindow()[q].ctrl) << '>';
288 else
289 VppLog() << "<unknown ctrl> ";
290 if(event->type == ClientMessage)
291 VppLog() << ": " << XAtomName(event->xclient.message_type);
292 VppLog() << '\n';
293 eo = true;
294 break;
295 }
296 #endif
297 DropStatusEvent(event);
298 if(q < 0) {
299 if(event->type == ButtonRelease && popupWnd)
300 popupWnd->SetFocus();
301 return;
302 }
303 XWindow& w = xmap[q];
304 if(w.ctrl) {
305 w.ctrl->EventProc(w, event);
306 if(w.ctrl)
307 w.ctrl->SyncMoves();
308 }
309 else
310 xmap.SetKey(q, None);
311 DefferedFocusSync();
312 #ifdef _DEBUG
313 #ifdef UPP_HEAP
314 if(MemoryCheck)
315 UPP::MemoryCheck();
316 #endif
317 if(eo)
318 LLOG("------ end of event processing ----------");
319 #endif
320 }
321
TimerAndPaint()322 void Ctrl::TimerAndPaint() {
323 GuiLock __;
324 LTIMING("TimerAndPaint");
325 TimerProc(msecs());
326 for(int i = 0; i < Xwindow().GetCount(); i++) {
327 XWindow& xw = Xwindow()[i];
328 if(Xwindow().GetKey(i) && xw.exposed && xw.invalid.GetCount()) {
329 if(xw.ctrl) {
330 LLOG("..and paint " << UPP::Name(xw.ctrl));
331 xw.ctrl->SyncScroll();
332 Vector<Rect> x = pick(xw.invalid);
333 xw.invalid.Clear();
334 xw.ctrl->DoPaint(x);
335 }
336 else
337 Xwindow().SetKey(i, None);
338 }
339 }
340 SyncCaret();
341 AnimateCaret();
342 XSync(Xdisplay, false);
343 SyncIMPosition();
344 }
345
ProcessEvent(bool *)346 bool Ctrl::ProcessEvent(bool *)
347 {
348 GuiLock __;
349 if(!IsWaitingEvent()) return false;
350 XEvent event;
351 XNextEvent(Xdisplay, &event);
352 ProcessEvent(&event);
353 return true;
354 }
355
356 void SweepMkImageCache();
357
ProcessEvents(bool *)358 bool Ctrl::ProcessEvents(bool *)
359 {
360 ASSERT_(IsMainThread(), "ProcessEvents can only run in the main thread");
361 GuiLock __;
362 if(ProcessEvent()) {
363 while(ProcessEvent() && (!LoopCtrl || LoopCtrl->InLoop())); // LoopCtrl-MF 071008
364 TimerAndPaint();
365 SweepMkImageCache();
366 return true;
367 }
368 TimerAndPaint();
369 SweepMkImageCache();
370 return false;
371 }
372
373 static bool WakePipeOK;
374 static int WakePipe[2];
375
376 INITBLOCK {
377 WakePipeOK = pipe(WakePipe) == 0;
378 fcntl(WakePipe[0], F_SETFL, O_NONBLOCK);
379 fcntl(WakePipe[1], F_SETFL, O_NONBLOCK);
380 }
381
382 void WakeUpGuiThread()
383 {
384 if(WakePipeOK)
385 IGNORE_RESULT(write(WakePipe[1], "\1", 1));
386 }
387
GuiSleep(int ms)388 void Ctrl::GuiSleep(int ms)
389 {
390 GuiLock __;
391 LLOG(msecs() << " GUISLEEP " << ms);
392 ASSERT_(IsMainThread(), "Only main thread can perform GuiSleep");
393 timeval timeout;
394 timeout.tv_sec = ms / 1000;
395 timeout.tv_usec = ms % 1000 * 1000;
396 XFlush(Xdisplay);
397 fd_set fdset;
398 FD_ZERO(&fdset);
399 FD_SET(Xconnection, &fdset);
400 if(WakePipeOK)
401 FD_SET(WakePipe[0], &fdset);
402 int level = LeaveGuiMutexAll(); // Give a chance to nonmain threads to touch GUI things
403 select((WakePipeOK ? max(WakePipe[0], Xconnection) : Xconnection) + 1, &fdset, NULL, NULL, &timeout);
404 char h;
405 while(WakePipeOK && read(WakePipe[0], &h, 1) > 0) // There might be relatively harmless race condition here causing delay in ICall
406 LLOG(msecs() << " WakeUpGuiThread detected!"); // or "void" passes through timer & paint
407 EnterGuiMutex(level);
408 }
409
410 static int granularity = 10;
411
SetTimerGranularity(int ms)412 void Ctrl::SetTimerGranularity(int ms)
413 {
414 GuiLock __;
415 granularity = ms;
416 }
417
SysEndLoop()418 void Ctrl::SysEndLoop()
419 {
420 }
421
EventLoop(Ctrl * ctrl)422 void Ctrl::EventLoop(Ctrl *ctrl)
423 {
424 GuiLock __;
425 ASSERT_(IsMainThread(), "EventLoop can only run in the main thread");
426 ASSERT(LoopLevel == 0 || ctrl);
427 LoopLevel++;
428 int64 loopno = ++EventLoopNo;
429 LLOG("Entering event loop at level " << LoopLevel << LOG_BEGIN);
430 Ctrl *ploop = NULL;
431 if(ctrl) {
432 ploop = LoopCtrl;
433 LoopCtrl = ctrl;
434 ctrl->inloop = true;
435 }
436
437 while(loopno > EndSessionLoopNo && (ctrl ? ctrl->InLoop() && ctrl->IsOpen() : GetTopCtrls().GetCount())) {
438 XEvent event;
439 GuiSleep(granularity);
440 SyncMousePos();
441 while(IsWaitingEvent()) {
442 LTIMING("XNextEvent");
443 XNextEvent(Xdisplay, &event);
444 ProcessEvent(&event);
445 }
446 TimerAndPaint();
447 SweepMkImageCache();
448 }
449
450 if(ctrl)
451 LoopCtrl = ploop;
452
453 LoopLevel--;
454 LLOG(LOG_END << "Leaving event loop ");
455 }
456
SyncExpose()457 void Ctrl::SyncExpose()
458 {
459 GuiLock __;
460 if(!top) return;
461 XEvent event;
462 while(top && XCheckTypedWindowEvent(Xdisplay, top->window, Expose, &event))
463 ProcessEvent(&event);
464 while(top && XCheckTypedWindowEvent(Xdisplay, top->window, GraphicsExpose, &event))
465 ProcessEvent(&event);
466 }
467
Create(Ctrl * owner,bool redirect,bool savebits)468 void Ctrl::Create(Ctrl *owner, bool redirect, bool savebits)
469 {
470 GuiLock __;
471 ASSERT_(IsMainThread(), "Only main thread can create windows");
472 LLOG("Create " << Name() << " " << GetRect());
473 ASSERT(!IsChild() && !IsOpen());
474 LLOG("Ungrab1");
475 ReleaseGrab();
476 XSetWindowAttributes swa;
477 swa.bit_gravity = ForgetGravity;
478 swa.background_pixmap = None;
479 swa.override_redirect = redirect;
480 swa.save_under = savebits;
481 Color c = SColorPaper();
482 swa.background_pixel = GetXPixel(c.GetR(), c.GetG(), c.GetB());
483 Rect r = GetRect();
484 isopen = true;
485 Window w = XCreateWindow(Xdisplay, RootWindow(Xdisplay, Xscreenno),
486 r.left, r.top, r.Width(), r.Height(),
487 0, CopyFromParent, InputOutput, CopyFromParent,
488 CWBitGravity|CWSaveUnder|CWOverrideRedirect|CWBackPixmap,
489 &swa);
490 if(!w) XError("XCreateWindow failed !");
491 int i = Xwindow().Find(None);
492 if(i >= 0) Xwindow().SetKey(i, w);
493 XWindow& cw = i >= 0 ? Xwindow()[i] : Xwindow().Add(w);
494 cw.ctrl = this;
495 cw.exposed = false;
496 cw.owner = owner;
497
498 cw.xic = NULL;
499
500 if(xim) {
501 cw.xic = XCreateIC(xim,
502 XNInputStyle, XIMPreeditNothing|XIMStatusNothing,
503 XNClientWindow, w,
504 NULL);
505 }
506 top = new Top;
507 top->window = w;
508 long im_event_mask = 0;
509 if(cw.xic)
510 XGetICValues(cw.xic, XNFilterEvents, &im_event_mask, NULL);
511 XSelectInput(Xdisplay, w, ExposureMask|StructureNotifyMask|KeyPressMask|
512 FocusChangeMask|KeyPressMask|KeyReleaseMask|PointerMotionMask|
513 ButtonPressMask|ButtonReleaseMask|PropertyChangeMask|
514 VisibilityChangeMask|im_event_mask);
515 int version = 5;
516 XChangeProperty(Xdisplay, w, XAtom("XdndAware"), XA_ATOM, 32,
517 0, (byte *)&version, 1);
518 CancelMode();
519 if(r.Contains(GetMousePos()))
520 DispatchMouse(MOUSEMOVE, GetMousePos() - r.TopLeft());
521
522 if(redirect) {
523 int windowType = XInternAtom(Xdisplay, "_NET_WM_WINDOW_TYPE_POPUP_MENU", false);
524 XChangeProperty(Xdisplay, w, XInternAtom(Xdisplay, "_NET_WM_WINDOW_TYPE", false), XA_ATOM, 32,
525 PropModeReplace, (byte *)&windowType, 1);
526 }
527
528 RefreshLayoutDeep();
529
530 SyncIMPosition();
531 }
532
WndDestroy()533 void Ctrl::WndDestroy()
534 {
535 GuiLock __;
536 LLOG("WndDestroy " << Name());
537 if(!top || !isopen) return;
538 AddGlobalRepaint();
539 bool revertfocus = HasWndFocus() || !GetFocusCtrl();
540 for(int i = 0; i < Xwindow().GetCount(); i++) {
541 LOGBEGIN();
542 XWindow& w = Xwindow()[i];
543 if(Xwindow().GetKey(i) != None && w.owner == this && w.ctrl->IsOpen())
544 w.ctrl->WndDestroy();
545 LOGEND();
546 }
547 Ptr<Ctrl> owner;
548 int i = Xwindow().Find(top->window);
549 if(i >= 0) {
550 XWindow& w = Xwindow()[i];
551 owner = w.owner;
552 w.invalid.Clear();
553 if(w.xic)
554 XDestroyIC(w.xic);
555 }
556 isopen = false;
557 if(focusWindow == top->window)
558 focusWindow = None;
559 if(grabWindow == top->window)
560 grabWindow = None;
561 XDestroyWindow(Xdisplay, top->window);
562 if(i >= 0) {
563 Xwindow().SetKey(i, None);
564 top->window = None;
565 Xwindow()[i].ctrl = NULL;
566 }
567
568 if(revertfocus && owner)
569 owner->TakeFocus();
570
571 if(focusWindow) {
572 int q = Xwindow().Find(focusWindow);
573 if(q >= 0) {
574 XIC xic = Xwindow()[q].xic;
575 if(xic) {
576 XSetICFocus(xic);
577 SyncIMPosition();
578 }
579 }
580 }
581
582 delete top;
583 top = NULL;
584 FocusSync();
585 }
586
GetTopCtrls()587 Vector<Ctrl *> Ctrl::GetTopCtrls()
588 {
589 GuiLock __;
590 Vector<Ctrl *> v;
591 const ArrayMap<Window, Ctrl::XWindow>& w = Xwindow();
592 for(int i = 0; i < w.GetCount(); i++)
593 if(w.GetKey(i) && w[i].ctrl && !w[i].ctrl->parent)
594 v.Add(w[i].ctrl);
595 return v;
596 }
597
StartPopupGrab()598 void Ctrl::StartPopupGrab()
599 {
600 GuiLock __;
601 if(PopupGrab == 0) {
602 if(!top) return;
603 if(XGrabPointer(
604 Xdisplay, top->window, true,
605 ButtonPressMask|ButtonReleaseMask|PointerMotionMask|EnterWindowMask|LeaveWindowMask,
606 GrabModeAsync, GrabModeAsync, None, None, CurrentTime) == GrabSuccess) {
607 PopupGrab++;
608 popupWnd = GetTopWindow();
609 #ifdef _DEBUG
610 __X11_Grabbing = true;
611 #endif
612 }
613 }
614 }
615
EndPopupGrab()616 void Ctrl::EndPopupGrab()
617 {
618 GuiLock __;
619 if(PopupGrab == 0) return;
620 if(--PopupGrab == 0) {
621 XUngrabPointer(Xdisplay, CurrentTime);
622 XFlush(Xdisplay);
623 #ifdef _DEBUG
624 __X11_Grabbing = false;
625 #endif
626 }
627 }
628
PopUp(Ctrl * owner,bool savebits,bool activate,bool,bool)629 void Ctrl::PopUp(Ctrl *owner, bool savebits, bool activate, bool, bool)
630 {
631 GuiLock __;
632 LLOG("POPUP: " << UPP::Name(this));
633 Ctrl *q = owner ? owner->GetTopCtrl() : GetActiveCtrl();
634 ignoretakefocus = true;
635 Create(q, true, savebits);
636 if(activate) {
637 q->StartPopupGrab();
638 popupgrab = true;
639 }
640 if(top) popup = true;
641 WndShow(visible);
642 if(activate && IsEnabled())
643 SetFocus();
644 if(top) top->owner = owner;
645 StateH(OPEN);
646 }
647
GetActiveCtrl()648 Ctrl *Ctrl::GetActiveCtrl()
649 {
650 GuiLock __;
651 return focusCtrl ? focusCtrl->GetTopCtrl() : NULL;
652 }
653
IsAlphaSupported()654 bool Ctrl::IsAlphaSupported()
655 {
656 return IsCompositedGui();
657 }
658
SetAlpha(byte alpha)659 void Ctrl::SetAlpha(byte alpha)
660 {
661 GuiLock __;
662 Window hwnd = GetWindow();
663 if (!IsAlphaSupported() || parent || !top || !hwnd)
664 return;
665 unsigned int opacity = (unsigned int) 16843009 * alpha;
666 Atom aw_opacity = XInternAtom(Xdisplay, "_NET_WM_WINDOW_OPACITY", XFalse);
667 XChangeProperty(Xdisplay, hwnd, aw_opacity, XA_CARDINAL, 32, PropModeReplace,
668 (unsigned char *) &opacity, 1);
669 }
670
IsCompositedGui()671 bool Ctrl::IsCompositedGui()
672 {
673 GuiLock __;
674 static bool b = XGetSelectionOwner(Xdisplay, XAtom(String().Cat() << "_NET_WM_CM_S" << Xscreenno)) != None;
675 return b;
676 }
677
GetOwner()678 Ctrl *Ctrl::GetOwner()
679 {
680 GuiLock __;
681 if(!IsOpen()) return NULL;
682 int q = Xwindow().Find(GetWindow());
683 return q >= 0 ? Xwindow()[q].owner : NULL;
684 }
685
WndShow(bool b)686 void Ctrl::WndShow(bool b)
687 {
688 GuiLock __;
689 LLOG("WndShow " << b);
690 if(top) {
691 XWindowAttributes xwa;
692 XGetWindowAttributes(Xdisplay, top->window, &xwa);
693 bool v = xwa.map_state == IsViewable;
694 if(b == v) return;
695 if(b)
696 XMapWindow(Xdisplay, top->window);
697 else
698 XUnmapWindow(Xdisplay, top->window);
699 visible = b;
700 StateH(SHOW);
701 }
702 }
703
WndUpdate()704 void Ctrl::WndUpdate()
705 {
706 GuiLock __;
707 LTIMING("WndUpdate");
708 LLOG("WNDUPDATE");
709 if(!top) return;
710 SyncExpose();
711 if(!top) return;
712 XWindow& xw = Xwindow().Get(top->window);
713 if(xw.exposed && xw.invalid.GetCount()) {
714 SyncScroll();
715 DoPaint(xw.invalid);
716 xw.invalid.Clear();
717 LTIMING("WndUpdate XSync");
718 XSync(Xdisplay, false);
719 }
720 }
721
WndUpdate(const Rect & r)722 void Ctrl::WndUpdate(const Rect& r)
723 {
724 GuiLock __;
725 LTIMING("WndUpdate Rect");
726 LLOG("WNDUPDATE " << r);
727 if(!top) return;
728 SyncExpose();
729 XWindow& xw = Xwindow().Get(top->window);
730 bool dummy;
731 SyncScroll();
732 DoPaint(Intersect(xw.invalid, r, dummy));
733 LTIMING("WndUpdate XSync");
734 XSync(Xdisplay, false);
735 xw.invalid = Subtract(xw.invalid, r, dummy);
736 }
737
WndSetPos(const Rect & r)738 void Ctrl::WndSetPos(const Rect& r)
739 {
740 GuiLock __;
741 if(!top) return;
742 LLOG("WndSetPos0 " << Name() << r);
743 AddGlobalRepaint();
744 XMoveResizeWindow(Xdisplay, top->window, r.left, r.top, r.Width(), r.Height());
745 rect = r;
746 SetWndRect(r);
747 }
748
IsWndOpen() const749 bool Ctrl::IsWndOpen() const
750 {
751 return top;
752 }
753
SetWndCapture()754 bool Ctrl::SetWndCapture()
755 {
756 GuiLock __;
757 if(!IsEnabled() || !top || !IsVisible()) return false;
758 if(top->window == grabWindow) return true;
759 int status = XGrabPointer(
760 Xdisplay, top->window, false,
761 ButtonPressMask|ButtonReleaseMask|PointerMotionMask|EnterWindowMask|LeaveWindowMask,
762 GrabModeAsync, GrabModeAsync, None, None, CurrentTime
763 );
764 if(status) return false;
765 #ifdef _DEBUG
766 __X11_Grabbing = true;
767 #endif
768 LLOG("Capture set ok");
769 grabWindow = top->window;
770 return true;
771 }
772
HasWndCapture() const773 bool Ctrl::HasWndCapture() const
774 {
775 GuiLock __;
776 return top && top->window == grabWindow;
777 }
778
ReleaseGrab()779 void Ctrl::ReleaseGrab()
780 {
781 GuiLock __;
782 if(grabWindow) {
783 LLOG("RELEASE GRAB");
784 XUngrabPointer(Xdisplay, CurrentTime);
785 XFlush(Xdisplay);
786 grabWindow = None;
787 #ifdef _DEBUG
788 __X11_Grabbing = false;
789 #endif
790 }
791 }
792
ReleaseWndCapture()793 bool Ctrl::ReleaseWndCapture()
794 {
795 GuiLock __;
796 LLOG("Releasing capture");
797 if(top && top->window == grabWindow) {
798 LLOG("Ungrab3");
799 ReleaseGrab();
800 return true;
801 }
802 return false;
803 }
804
SetMouseCursor(const Image & image)805 void Ctrl::SetMouseCursor(const Image& image)
806 {
807 GuiLock __;
808 static Image img;
809 static Cursor shc;
810 if(img.GetSerialId() != image.GetSerialId()) {
811 img = image;
812 Cursor hc = (Cursor)CursorX11(IsNull(img) ? Image::Arrow() : img);
813 for(int i = 0; i < Xwindow().GetCount(); i++) {
814 Window wnd = Xwindow().GetKey(i);
815 if(wnd)
816 XDefineCursor(Xdisplay, wnd, hc);
817 }
818 if(shc)
819 XFreeCursor(Xdisplay, shc);
820 shc = hc;
821 XFlush(Xdisplay);
822 }
823 }
824
825 void ClearKbdState_();
826
TakeFocus()827 void Ctrl::TakeFocus()
828 {
829 LLOG("TAKE_FOCUS0 " << Name());
830 GuiLock __;
831 if(IsChild()) {
832 Ctrl *w = GetTopCtrl();
833 if(w) w->TakeFocus();
834 return;
835 }
836 XWindow *w = GetXWindow();
837 if(!w)
838 return;
839 if(ignoretakefocus) {
840 LLOG("IGNORED TAKE_FOCUS (caused by CreateWindow)");
841 if(focusWindow != top->window && focusWindow != None)
842 XSetInputFocus(Xdisplay, focusWindow, RevertToParent, CurrentTime);
843 return;
844 }
845 if(w->owner) {
846 LLOG("IGNORED TAKE_FOCUS (window is owned)");
847 return;
848 }
849 LLOG("TAKE_FOCUS " << Name());
850 if(IsEnabled() && IsVisible() && top->window != GetXServerFocusWindow()) {
851 ClearKbdState_();
852 SetWndFocus();
853 }
854 if(this != focusCtrlWnd) {
855 if(IsEnabled()) {
856 SetLastActive(w, this);
857 LLOG("Activate " << Name());
858 SetFocusWnd();
859 }
860 else {
861 LLOG("Activate of disabled window" << Name());
862 if(w->last_active && w->last_active->IsOpen() && w->last_active->IsEnabled() &&
863 w->last_active != this) {
864 LLOG(" activating last active: " << UPP::Name(w->last_active));
865 w->last_active->TakeFocus();
866 }
867 }
868 }
869
870 return;
871 }
872
KillFocus(Window window)873 void Ctrl::KillFocus(Window window)
874 {
875 GuiLock __;
876 if(!window)
877 return;
878 int q = Xwindow().Find(window);
879 if(q < 0)
880 return;
881 XWindow& w = Xwindow()[q];
882 if(w.ctrl)
883 w.ctrl->KillFocusWnd();
884 }
885
SetWndFocus()886 bool Ctrl::SetWndFocus()
887 {
888 GuiLock __;
889 LLOG("SetWndFocus " << Name());
890 if(top && top->window != focusWindow && IsEnabled() && IsVisible()) {
891 LLOG("Setting focus... ");
892 LTIMING("XSetInfputFocus");
893 Ptr<Ctrl> _this = this;
894 KillFocus(focusWindow);
895 if(_this && top) {
896 XSetInputFocus(Xdisplay, top->window, RevertToParent, CurrentTime);
897 focusWindow = top->window;
898 SetFocusWnd();
899 }
900 return true;
901 }
902 return false;
903 }
904
HasWndFocus() const905 bool Ctrl::HasWndFocus() const
906 {
907 GuiLock __;
908 return top && top->window == focusWindow;
909 }
910
GetXServerFocusWindow()911 Window Ctrl::GetXServerFocusWindow()
912 {
913 GuiLock __;
914 Window w;
915 int dummy;
916 XGetInputFocus(Xdisplay, &w, &dummy);
917 return w;
918 }
919
FocusSync()920 void Ctrl::FocusSync()
921 {
922 GuiLock __;
923 Window fw = GetXServerFocusWindow();
924 if(fw != focusWindow) {
925 LLOG("FocusSync to " << FormatIntHex(fw));
926 if(fw) {
927 int q = Xwindow().Find(fw);
928 if(q >= 0) {
929 LLOG("Focus to app window");
930 Window fwo = focusWindow;
931 focusWindow = fw;
932 XWindow& w = Xwindow()[q];
933 if(w.ctrl) {
934 if(w.ctrl->IsPopUp()) {
935 AnimateCaret();
936 return;
937 }
938 KillFocus(fwo);
939 // focusWindow = None; // 1010-10-13 cxl (SetWndFocus does not set this)
940 w.ctrl->SetFocusWnd();
941 AnimateCaret();
942 return;
943 }
944 }
945 }
946 KillFocus(focusWindow);
947 focusWindow = None;
948 AnimateCaret();
949 }
950 }
951
SyncIMPosition()952 void Ctrl::SyncIMPosition()
953 {
954 /*
955 if(!focusWindow)
956 return;
957 int q = Xwindow().Find(focusWindow);
958 if(q < 0)
959 return;
960 XWindow& xw = Xwindow()[q];
961 XIC xic = xw.xic;
962 if(xw.xic && xw.ctrl) {
963 XVaNestedList preedit_attr;
964 XPoint spot;
965 spot.x = xw.ctrl->caretx + xw.ctrl->caretcx;
966 spot.y = xw.ctrl->carety + xw.ctrl->caretcy;
967 preedit_attr = XVaCreateNestedList(0, XNSpotLocation, &spot, NULL);
968 XSetICValues(xw.xic, XNPreeditAttributes, preedit_attr, NULL);
969 XFree(preedit_attr);
970 }
971 */
972 }
973
AnimateCaret()974 void Ctrl::AnimateCaret()
975 {
976 GuiLock __;
977 int v = !(((msecs() - WndCaretTime) / 500) & 1);
978 if(v != WndCaretVisible) {
979 RefreshCaret();
980 WndCaretVisible = v;
981 }
982 }
983
Invalidate(XWindow & xw,const Rect & _r)984 void Ctrl::Invalidate(XWindow& xw, const Rect& _r)
985 {
986 GuiLock __;
987 LTIMING("Invalidate");
988 LLOG("Invalidate " << UPP::Name(xw.ctrl) << " " << _r);
989 AddRefreshRect(xw.invalid, _r);
990 }
991
AddGlobalRepaint()992 void Ctrl::AddGlobalRepaint()
993 {
994 GuiLock __;
995 Rect rect = GetRect();
996 rect.Inflate(32, 32);//TODO !!! Not correct !!! should read frame extent instead... - or shift GraphicsExposes / NoExposes...
997 ArrayMap<Window, Ctrl::XWindow>& w = Xwindow();
998 for(int i = 0; i < w.GetCount(); i++)
999 if(w.GetKey(i) && w[i].ctrl) {
1000 const Rect& r = w[i].ctrl->rect;
1001 if(rect.Intersects(r))
1002 Invalidate(w[i], Rect(rect.Offseted(-r.TopLeft())));
1003 }
1004 }
1005
WndInvalidateRect(const Rect & r)1006 void Ctrl::WndInvalidateRect(const Rect& r)
1007 {
1008 GuiLock __;
1009 if(!top) return;
1010 LLOG("WndInvalidateRect0 " << r);
1011 Invalidate(Xwindow().Get(top->window), r);
1012 }
1013
SetWndForeground()1014 void Ctrl::SetWndForeground()
1015 {
1016 GuiLock __;
1017 LLOG("SetWndForeground " << Name());
1018 if(!top || !IsVisible()) return;
1019 if(!IsEnabled()) {
1020 LLOG("Not enabled");
1021 XWindow *w = GetXWindow();
1022 if(w && w->last_active && w->last_active != this &&
1023 w->last_active->IsOpen() && w->last_active->IsEnabled())
1024 w->last_active->SetWndForeground();
1025 }
1026 else {
1027 Ptr<Ctrl> _this = this;
1028 SetWndFocus();
1029 if(_this && top)
1030 XRaiseWindow(Xdisplay, top->window);
1031 }
1032 }
1033
IsWndForeground() const1034 bool Ctrl::IsWndForeground() const
1035 {
1036 GuiLock __;
1037 const Ctrl *q = GetTopWindow();
1038 return ~focusCtrlWnd == (q ? q : GetTopCtrl());
1039 }
1040
WndEnable(bool b)1041 void Ctrl::WndEnable(bool b)
1042 {
1043 GuiLock __;
1044 LLOG("WndEnable");
1045 if(!top) return;
1046 if(!b) {
1047 ReleaseCapture();
1048 if(HasWndFocus())
1049 XSetInputFocus(Xdisplay, None, RevertToPointerRoot, CurrentTime);
1050 }
1051 }
1052
1053 // 01/12/2007 - mdelfede
1054 // added support for windowed controls
AddXWindow(Window & w)1055 Ctrl::XWindow *Ctrl::AddXWindow(Window &w)
1056 {
1057 GuiLock __;
1058 int i = Xwindow().Find(None);
1059 if(i >= 0)
1060 Xwindow().SetKey(i, w);
1061 XWindow& cw = i >= 0 ? Xwindow()[i] : Xwindow().Add(w);
1062 cw.ctrl = this;
1063 cw.exposed = true;
1064 cw.owner = GetParent();
1065 cw.xic = NULL;
1066 return &cw;
1067 }
1068
RemoveXWindow(Window & w)1069 void Ctrl::RemoveXWindow(Window &w)
1070 {
1071 GuiLock __;
1072 int i = Xwindow().Find(w);
1073 if(i >= 0) {
1074 Xwindow().SetKey(i, None);
1075 Xwindow()[i].ctrl = NULL;
1076 }
1077
1078 }
XWindowFromWindow(Window & w)1079 Ctrl::XWindow *Ctrl::XWindowFromWindow(Window &w)
1080 {
1081 GuiLock __;
1082 int i = Xwindow().Find(w);
1083 if(i >= 0)
1084 return &Xwindow()[i];
1085 else
1086 return NULL;
1087 }
1088
1089 // Synchronizes the native windows inside ctrls
SyncNativeWindows(void)1090 void Ctrl::SyncNativeWindows(void)
1091 {
1092 GuiLock __;
1093 ArrayMap<Window, Ctrl::XWindow>& xwindows = Xwindow();
1094 for(int i = 0; i < xwindows.GetCount(); i++)
1095 {
1096 XWindow &xw = xwindows[i];
1097 Window w = xwindows.GetKey(i);
1098 if(xw.ctrl && xw.ctrl->parent && w)
1099 {
1100 Window dummy;
1101 int x, y;
1102 unsigned int width, height, border, depth;
1103 XGetGeometry(Xdisplay, w, &dummy, &x, &y, &width, &height, &border, &depth);
1104 Rect r = xw.ctrl->GetRectInParentWindow();
1105 if( (x != r.left || y != r.top) && ((int)width == r.Width() && (int)height == r.Height()))
1106 XMoveWindow(Xdisplay, w, r.left, r.top);
1107 else if( (x == r.left && y == r.top) && ((int)width != r.Width() || (int)height != r.Height()))
1108 XResizeWindow(Xdisplay, w, r.Width(), r.Height());
1109 else if( x != r.left || y != r.top || (int)width != r.Width() || (int)height != r.Height())
1110 XMoveResizeWindow(Xdisplay, w, r.left, r.top, r.Width(), r.Height());
1111 }
1112 }
1113
1114 } // END Ctrl::SyncNativeWindows()
1115
1116 // 01/12/2007 - END
1117
TopFrameDraw(Ctrl * ctrl,const Rect & r)1118 TopFrameDraw::TopFrameDraw(Ctrl *ctrl, const Rect& r)
1119 {
1120 EnterGuiMutex();
1121 Ctrl *top = ctrl->GetTopCtrl();
1122 Vector<Rect> clip;
1123 clip.Add(r);
1124 dw = top->GetWindow();
1125 gc = XCreateGC(Xdisplay, dw, 0, 0);
1126 xftdraw = XftDrawCreate(Xdisplay, (Drawable) dw,
1127 DefaultVisual(Xdisplay, Xscreenno), Xcolormap);
1128 Init(clip, r.TopLeft());
1129 }
1130
~TopFrameDraw()1131 TopFrameDraw::~TopFrameDraw()
1132 {
1133 XFreeGC(Xdisplay, gc);
1134 LeaveGuiMutex();
1135 }
1136
1137 }
1138
1139 #endif
1140