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