1 /*
2 * IceWM
3 *
4 * Copyright (C) 1997-2002 Marko Macek
5 */
6 #include "config.h"
7 #include "yfull.h"
8 #include "ywindow.h"
9 #include "ykey.h"
10
11 #include "ytooltip.h"
12 #include "yxapp.h"
13 #include "sysdep.h"
14 #include "yprefs.h"
15 #include "yrect.h"
16 #include "ascii.h"
17
18 #include "ytimer.h"
19 #include "ypopup.h"
20 #include "yxcontext.h"
21 #include <typeinfo>
22
23 #ifdef XINERAMA
24 #include <X11/extensions/Xinerama.h>
25 #endif
26
27 /******************************************************************************/
28 /******************************************************************************/
29
30 class YAutoScroll: public YTimerListener {
31 public:
32 YAutoScroll();
33 virtual ~YAutoScroll();
34
35 virtual bool handleTimer(YTimer *timer);
36
37 void autoScroll(YWindow *w, bool autoScroll, const XMotionEvent *motion);
38
getWindow() const39 YWindow *getWindow() const { return fWindow; }
isScrolling() const40 bool isScrolling() const { return fScrolling; }
41 private:
42 YTimer fAutoScrollTimer;
43 XMotionEvent fMotion;
44 YWindow *fWindow;
45 bool fScrolling;
46 };
47
48
YAutoScroll()49 YAutoScroll::YAutoScroll() :
50 fAutoScrollTimer(autoScrollDelay, this, false)
51 {
52 fWindow = nullptr;
53 fScrolling = false;
54 }
55
~YAutoScroll()56 YAutoScroll::~YAutoScroll() {
57 }
58
handleTimer(YTimer * timer)59 bool YAutoScroll::handleTimer(YTimer *timer) {
60 if (fWindow) {
61 return fWindow->handleAutoScroll(fMotion);
62 }
63 return false;
64 }
65
autoScroll(YWindow * w,bool autoScroll,const XMotionEvent * motion)66 void YAutoScroll::autoScroll(YWindow *w, bool autoScroll, const XMotionEvent *motion) {
67 if (motion)
68 fMotion = *motion;
69 else
70 w = nullptr;
71 fWindow = w;
72 if (w == nullptr)
73 autoScroll = false;
74 fScrolling = autoScroll;
75 if (autoScroll) {
76 fAutoScrollTimer.startTimer();
77 } else {
78 fAutoScrollTimer.stopTimer();
79 }
80 }
81
82 /******************************************************************************/
83 /******************************************************************************/
84
85 YAutoScroll *YWindow::fAutoScroll = nullptr;
86 YWindow *YWindow::fClickWindow = nullptr;
87 Time YWindow::fClickTime = 0;
88 int YWindow::fClickCount = 0;
89 XButtonEvent YWindow::fClickEvent;
90 bool YWindow::fClickDrag;
91 unsigned int YWindow::fClickButton = 0;
92 unsigned int YWindow::fClickButtonDown = 0;
93
94 unsigned long YWindow::lastEnterNotifySerial; // credits to ahwm
getLastEnterNotifySerial()95 unsigned long YWindow::getLastEnterNotifySerial() {
96 return lastEnterNotifySerial;
97 }
updateEnterNotifySerial(const XEvent & event)98 void YWindow::updateEnterNotifySerial(const XEvent &event) {
99 lastEnterNotifySerial = event.xany.serial;
100 xapp->sync();
101 }
102
103 /******************************************************************************/
104 /******************************************************************************/
105
YWindow(YWindow * parent,Window win,int depth,Visual * visual,Colormap colormap)106 YWindow::YWindow(YWindow *parent, Window win, int depth,
107 Visual *visual, Colormap colormap):
108 fDepth(depth ? unsigned(depth) : xapp->depth()),
109 fVisual(visual ? visual : xapp->visual()),
110 fColormap(colormap),
111 fParentWindow(parent),
112 fFocusedWindow(nullptr),
113 fHandle(win), flags(0), fStyle(0),
114 fX(0), fY(0), fWidth(1), fHeight(1),
115 unmapCount(0),
116 fPointer(),
117 fGraphics(nullptr),
118 fEventMask(KeyPressMask|KeyReleaseMask|FocusChangeMask|
119 LeaveWindowMask|EnterWindowMask),
120 fWinGravity(NorthWestGravity), fBitGravity(ForgetGravity),
121 accel(nullptr)
122 {
123 if (fHandle != None) {
124 MSG(("adopting window %lX", fHandle));
125 flags |= wfAdopted;
126 adopt();
127 }
128 else if (fParentWindow == nullptr) {
129 PRECONDITION(desktop);
130 fParentWindow = desktop;
131 }
132 insertWindow();
133 }
134
~YWindow()135 YWindow::~YWindow() {
136 if (fAutoScroll &&
137 fAutoScroll->getWindow() == this)
138 {
139 delete fAutoScroll;
140 fAutoScroll = nullptr;
141 }
142 fFocusedWindow = nullptr;
143 removeWindow();
144 while (nextWindow() != nullptr)
145 nextWindow()->removeWindow();
146 while (accel) {
147 YAccelerator *next = accel->next;
148 delete accel;
149 accel = next;
150 }
151 if (fClickWindow == this)
152 fClickWindow = nullptr;
153 if (fGraphics) {
154 delete fGraphics; fGraphics = nullptr;
155 }
156 if (flags & wfCreated)
157 destroy();
158 }
159
colormap()160 Colormap YWindow::colormap() {
161 if (fColormap == None) {
162 if (fParentWindow
163 && (fVisual == CopyFromParent || fVisual == fParentWindow->fVisual)
164 && (fDepth == CopyFromParent || fDepth == fParentWindow->fDepth))
165 {
166 fColormap = fParentWindow->colormap();
167 }
168 if (fVisual && fColormap == None) {
169 fColormap = xapp->colormapForVisual(fVisual);
170 }
171 if (fDepth && fColormap == None) {
172 fColormap = xapp->colormapForDepth(fDepth);
173 }
174 if (fColormap == None) {
175 // unexpected
176 fColormap = XCreateColormap(xapp->display(), xapp->root(),
177 fVisual ? fVisual : xapp->visual(),
178 AllocNone);
179 }
180 }
181 return fColormap;
182 }
183
setWindowFocus(Time timestamp)184 void YWindow::setWindowFocus(Time timestamp) {
185 XSetInputFocus(xapp->display(), handle(), RevertToNone, timestamp);
186 }
187
setTitle(char const * title)188 void YWindow::setTitle(char const * title) {
189 XStoreName(xapp->display(), handle(), title);
190 }
191
fetchTitle(char ** title)192 bool YWindow::fetchTitle(char** title) {
193 return XFetchName(xapp->display(), handle(), title);
194 }
195
setClassHint(char const * rName,char const * rClass)196 void YWindow::setClassHint(char const * rName, char const * rClass) {
197 XClassHint wmclass;
198 wmclass.res_name = const_cast<char *>(rName);
199 wmclass.res_class = const_cast<char *>(rClass);
200
201 XSetClassHint(xapp->display(), handle(), &wmclass);
202 }
203
setStyle(unsigned aStyle)204 void YWindow::setStyle(unsigned aStyle) {
205 if (fStyle != aStyle) {
206 fStyle = aStyle;
207
208 if (flags & wfCreated) {
209 if (fStyle & wsToolTip)
210 fEventMask = ExposureMask;
211
212 if (fStyle & wsPointerMotion)
213 fEventMask |= PointerMotionMask;
214
215
216 if (hasbit(fStyle, wsDesktopAware | wsManager) ||
217 (fHandle != xapp->root()))
218 fEventMask |=
219 StructureNotifyMask |
220 ColormapChangeMask |
221 PropertyChangeMask;
222
223 if (fStyle & wsManager)
224 fEventMask |=
225 FocusChangeMask |
226 SubstructureRedirectMask | SubstructureNotifyMask;
227
228 fEventMask |= ButtonPressMask | ButtonReleaseMask | ButtonMotionMask;
229 if (fHandle == xapp->root())
230 if (!(fStyle & wsManager) || !grabRootWindow)
231 fEventMask &= ~(ButtonPressMask | ButtonReleaseMask | ButtonMotionMask);
232
233 if (fStyle & wsNoExpose)
234 fEventMask &= ~ExposureMask;
235
236 XSelectInput(xapp->display(), fHandle, fEventMask);
237 }
238 }
239 }
240
addEventMask(long mask)241 void YWindow::addEventMask(long mask) {
242 if (hasbits(fEventMask, mask) == false) {
243 fEventMask |= mask;
244 if (flags & wfCreated)
245 XSelectInput(xapp->display(), fHandle, fEventMask);
246 }
247 }
248
getGraphics()249 Graphics &YWindow::getGraphics() {
250 return *(fGraphics ? fGraphics : fGraphics = new Graphics(*this));
251 }
252
repaint()253 void YWindow::repaint() {
254 XClearArea(xapp->display(), handle(), 0, 0, 0, 0, True);
255 }
256
repaintFocus()257 void YWindow::repaintFocus() {
258 repaint();
259 }
260
getWindowAttributes(XWindowAttributes * attr)261 bool YWindow::getWindowAttributes(XWindowAttributes* attr) {
262 if (fHandle == None)
263 return false;
264
265 if (XGetWindowAttributes(xapp->display(), fHandle, attr))
266 return true;
267
268 setDestroyed();
269 return false;
270 }
271
readAttributes()272 void YWindow::readAttributes() {
273 XWindowAttributes attributes;
274
275 if (!getWindowAttributes(&attributes))
276 return;
277
278 fX = attributes.x;
279 fY = attributes.y;
280 fWidth = unsigned(attributes.width);
281 fHeight = unsigned(attributes.height);
282
283 if (fHandle != xapp->root()) {
284 fDepth = attributes.depth;
285 fVisual = attributes.visual;
286 fColormap = attributes.colormap;
287 }
288
289 //MSG(("window initial geometry (%d:%d %dx%d)",
290 // fX, fY, fWidth, fHeight));
291
292 if (attributes.map_state != IsUnmapped)
293 flags |= wfVisible;
294 else
295 flags &= unsigned(~wfVisible);
296 }
297
create()298 Window YWindow::create() {
299 if (flags & wfCreated)
300 return fHandle;
301
302 XSetWindowAttributes attributes = { 0, };
303 unsigned int attrmask = CWEventMask;
304 const bool output = notbit(fStyle, wsInputOnly);
305
306 fEventMask |=
307 ExposureMask |
308 ButtonPressMask | ButtonReleaseMask | ButtonMotionMask;
309
310 if (fStyle & wsToolTip)
311 fEventMask = ExposureMask;
312
313 if (fStyle & wsPointerMotion)
314 fEventMask |= PointerMotionMask;
315
316 if (fParentWindow == desktop && !(fStyle & wsOverrideRedirect))
317 fEventMask |= StructureNotifyMask | SubstructureRedirectMask;
318 if (fStyle & wsManager)
319 fEventMask |= SubstructureRedirectMask | SubstructureNotifyMask;
320 if (fStyle & (wsInputOnly | wsNoExpose)) {
321 fEventMask &= ~(ExposureMask);
322 if (fStyle & wsInputOnly)
323 fEventMask &= ~(FocusChangeMask);
324 }
325
326 if (fStyle & wsSaveUnder) {
327 attributes.save_under = True;
328 attrmask |= CWSaveUnder;
329 }
330 if (fStyle & wsOverrideRedirect) {
331 attributes.override_redirect = True;
332 attrmask |= CWOverrideRedirect;
333 }
334 if (fStyle & wsBackingMapped) {
335 attributes.backing_store = WhenMapped;
336 attrmask |= CWBackingStore;
337 }
338 if (fPointer) {
339 attrmask |= CWCursor;
340 attributes.cursor = fPointer;
341 }
342 if (fBitGravity != ForgetGravity) {
343 attributes.bit_gravity = fBitGravity;
344 attrmask |= CWBitGravity;
345 }
346 if (fWinGravity != NorthWestGravity) {
347 attributes.win_gravity = fWinGravity;
348 attrmask |= CWWinGravity;
349 }
350 if (fDepth && output) {
351 attributes.background_pixel = xapp->black();
352 attrmask |= CWBackPixel;
353 attributes.border_pixel = xapp->black();
354 attrmask |= CWBorderPixel;
355 }
356 if (fVisual && output) {
357 attributes.colormap = colormap();
358 attrmask |= CWColormap;
359 }
360
361 attributes.event_mask = fEventMask;
362 unsigned zw = width();
363 unsigned zh = height();
364 if (zw == 0 || zh == 0) {
365 zw = 1;
366 zh = 1;
367 flags |= wfNullSize;
368 }
369 fHandle = XCreateWindow(xapp->display(),
370 parent()->handle(),
371 x(), y(), zw, zh,
372 0,
373 output ? fDepth : CopyFromParent,
374 output ? InputOutput : InputOnly,
375 output ? fVisual : CopyFromParent,
376 attrmask,
377 &attributes);
378
379 XWindowAttributes wa;
380 if (XGetWindowAttributes(xapp->display(), fHandle, &wa) == False) {
381 flags |= (wfCreated | wfDestroyed);
382 return None;
383 }
384 fDepth = unsigned(wa.depth);
385 fVisual = wa.visual;
386 if (parent() == desktop && !(flags & (wsManager | wsOverrideRedirect))) {
387 Atom prot[] = { _XA_WM_DELETE_WINDOW, _XA_WM_TAKE_FOCUS };
388 const int n = 1 + hasbit(flags, wsTakeFocus);
389 XSetWMProtocols(xapp->display(), fHandle, prot, n);
390 }
391
392 if ((flags & wfVisible) && !(flags & wfNullSize))
393 XMapWindow(xapp->display(), fHandle);
394
395 windowContext.save(fHandle, this);
396 flags |= wfCreated;
397
398 return fHandle;
399 }
400
adopt()401 void YWindow::adopt() {
402 readAttributes();
403
404 fEventMask = 0;
405
406 if ((fStyle & wsDesktopAware) || (fStyle & wsManager) ||
407 (fHandle != xapp->root()))
408 fEventMask |=
409 StructureNotifyMask |
410 ColormapChangeMask |
411 PropertyChangeMask;
412
413 if (fStyle & wsManager) {
414 fEventMask |=
415 FocusChangeMask |
416 SubstructureRedirectMask | SubstructureNotifyMask |
417 ButtonPressMask | ButtonReleaseMask | ButtonMotionMask;
418
419
420 if (!grabRootWindow && fHandle == xapp->root()) {
421 fEventMask &= ~(ButtonPressMask | ButtonReleaseMask | ButtonMotionMask);
422 }
423 }
424
425 if (destroyed() == false)
426 XSelectInput(xapp->display(), fHandle, fEventMask);
427
428 windowContext.save(fHandle, this);
429 flags |= wfCreated;
430 }
431
destroy()432 void YWindow::destroy() {
433 if (flags & wfCreated) {
434 if (!(flags & wfDestroyed)) {
435 setDestroyed();
436 if (!(flags & wfAdopted)) {
437 MSG(("----------------------destroy %lX", fHandle));
438 XDestroyWindow(xapp->display(), fHandle);
439 removeAllIgnoreUnmap();
440 } else {
441 XSelectInput(xapp->display(), fHandle, NoEventMask);
442 }
443 }
444 windowContext.remove(fHandle);
445 fHandle = None;
446 flags &= unsigned(~wfCreated);
447 }
448 }
removeWindow()449 void YWindow::removeWindow() {
450 if (fParentWindow) {
451 fParentWindow->remove(this);
452 fParentWindow = nullptr;
453 }
454 }
455
insertWindow()456 void YWindow::insertWindow() {
457 if (fParentWindow) {
458 fParentWindow->prepend(this);
459 }
460 }
461
reparent(YWindow * parent,int x,int y)462 void YWindow::reparent(YWindow *parent, int x, int y) {
463 // ensure window was created before reparenting
464 (void) handle();
465 if ((flags & (wfVisible | wfDestroyed)) == wfVisible) {
466 addIgnoreUnmap();
467 }
468
469 removeWindow();
470 fParentWindow = parent;
471 insertWindow();
472
473 if (notbit(flags, wfDestroyed)) {
474 MSG(("--- reparent %lX to %lX", handle(), parent->handle()));
475 XReparentWindow(xapp->display(), handle(), parent->handle(), x, y);
476 }
477 fX = x;
478 fY = y;
479 }
480
show()481 void YWindow::show() {
482 if (!(flags & (wfVisible | wfDestroyed))) {
483 flags |= wfVisible;
484 if (!(flags & wfNullSize))
485 XMapWindow(xapp->display(), handle());
486 }
487 }
488
hide()489 void YWindow::hide() {
490 if (flags & wfVisible) {
491 flags &= unsigned(~wfVisible);
492 if (!(flags & (wfNullSize | wfDestroyed))) {
493 addIgnoreUnmap();
494 XUnmapWindow(xapp->display(), handle());
495 }
496 }
497 }
498
setDestroyed()499 void YWindow::setDestroyed() {
500 flags |= wfDestroyed;
501 }
502
testDestroyed()503 bool YWindow::testDestroyed() {
504 XWindowAttributes attr;
505 return fHandle && (destroyed() || !getWindowAttributes(&attr));
506 }
507
setVisible(bool enable)508 void YWindow::setVisible(bool enable) {
509 return enable ? show() : hide();
510 }
511
setWinGravity(int gravity)512 void YWindow::setWinGravity(int gravity) {
513 if (flags & wfCreated) {
514 unsigned long eventmask = CWWinGravity;
515 XSetWindowAttributes attributes;
516 attributes.win_gravity = gravity;
517 fWinGravity = gravity;
518
519 XChangeWindowAttributes(xapp->display(), handle(), eventmask, &attributes);
520 } else {
521 fWinGravity = gravity;
522 }
523 }
524
setBitGravity(int gravity)525 void YWindow::setBitGravity(int gravity) {
526 if (flags & wfCreated) {
527 unsigned long eventmask = CWBitGravity;
528 XSetWindowAttributes attributes;
529 attributes.bit_gravity = gravity;
530 fBitGravity = gravity;
531
532 XChangeWindowAttributes(xapp->display(), handle(), eventmask, &attributes);
533 } else {
534 fBitGravity = gravity;
535 }
536 }
537
raise()538 void YWindow::raise() {
539 XRaiseWindow(xapp->display(), handle());
540 }
541
lower()542 void YWindow::lower() {
543 XLowerWindow(xapp->display(), handle());
544 }
545
beneath(YWindow * superior)546 void YWindow::beneath(YWindow* superior) {
547 if (superior) {
548 Window stack[] = { superior->handle(), handle(), };
549 XRestackWindows(xapp->display(), stack, 2);
550 }
551 }
552
raiseTo(YWindow * inferior)553 void YWindow::raiseTo(YWindow* inferior) {
554 if (inferior) {
555 unsigned mask = CWSibling | CWStackMode;
556 XWindowChanges xwc;
557 xwc.sibling = inferior->handle();
558 xwc.stack_mode = Above;
559 XConfigureWindow(xapp->display(), handle(), mask, &xwc);
560 }
561 }
562
handleEvent(const XEvent & event)563 void YWindow::handleEvent(const XEvent &event) {
564 switch (event.type) {
565 case KeyPress:
566 case KeyRelease:
567 {
568 for (YWindow *w = this; // !!! hack, fix
569 w && w->handleKey(event.xkey) == false;
570 w = w->parent()) {}
571 }
572 break;
573
574 case ButtonPress:
575 captureEvents();
576 handleButton(event.xbutton);
577 break;
578
579 case ButtonRelease:
580 releaseEvents();
581 handleButton(event.xbutton);
582 break;
583
584 case MotionNotify:
585 {
586 const long mask = KeyPressMask |
587 KeyReleaseMask |
588 ButtonPressMask |
589 ButtonReleaseMask |
590 ButtonMotionMask;
591 XEvent old_event(event), new_event;
592
593 while (XCheckMaskEvent(xapp->display(), mask, &new_event)) {
594 if (event.type != new_event.type ||
595 event.xmotion.window != new_event.xmotion.window ||
596 event.xmotion.subwindow != new_event.xmotion.subwindow)
597 {
598 XPutBackEvent(xapp->display(), &new_event);
599 break;
600 } else {
601 xapp->sync();
602 old_event = new_event;
603 }
604 }
605 handleMotion(old_event.xmotion);
606 }
607 break;
608
609 case EnterNotify:
610 case LeaveNotify:
611 handleCrossing(event.xcrossing);
612 break;
613
614 case FocusIn:
615 case FocusOut:
616 handleFocus(event.xfocus);
617 break;
618
619 case PropertyNotify:
620 handleProperty(event.xproperty);
621 break;
622
623 case ColormapNotify:
624 handleColormap(event.xcolormap);
625 break;
626
627 case MapRequest:
628 handleMapRequest(event.xmaprequest);
629 break;
630
631 case ReparentNotify:
632 handleReparentNotify(event.xreparent);
633 break;
634
635 case ConfigureNotify:
636 updateEnterNotifySerial(event);
637 #if 1
638 {
639 XEvent new_event, old_event;
640
641 old_event = event;
642 while (/*XPending(app->display()) > 0 &&*/
643 XCheckTypedWindowEvent(xapp->display(), handle(), ConfigureNotify,
644 &new_event) == True)
645 {
646 if (event.type != new_event.type ||
647 event.xconfigure.window != new_event.xconfigure.window)
648 {
649 XPutBackEvent(xapp->display(), &new_event);
650 break;
651 } else {
652 XFlush(xapp->display());
653 old_event = new_event;
654 }
655 }
656 handleConfigure(old_event.xconfigure);
657 }
658 #else
659 handleConfigure(event.xconfigure);
660 #endif
661 break;
662
663 case ConfigureRequest:
664 handleConfigureRequest(event.xconfigurerequest);
665 break;
666
667 case DestroyNotify:
668 handleDestroyWindow(event.xdestroywindow);
669 break;
670
671 case Expose:
672 handleExpose(event.xexpose);
673 break;
674
675 case GraphicsExpose:
676 handleGraphicsExpose(event.xgraphicsexpose);
677 break;
678
679 case MapNotify:
680 updateEnterNotifySerial(event);
681 handleMapNotify(event.xmap);
682 break;
683
684 case UnmapNotify:
685 updateEnterNotifySerial(event);
686 handleUnmapNotify(event.xunmap);
687 break;
688
689 case ClientMessage:
690 handleClientMessage(event.xclient);
691 break;
692
693 case SelectionClear:
694 handleSelectionClear(event.xselectionclear);
695 break;
696
697 case SelectionRequest:
698 handleSelectionRequest(event.xselectionrequest);
699 break;
700
701 case SelectionNotify:
702 handleSelection(event.xselection);
703 break;
704
705 case GravityNotify:
706 updateEnterNotifySerial(event);
707 handleGravityNotify(event.xgravity);
708 break;
709
710 case CirculateNotify:
711 updateEnterNotifySerial(event);
712 break;
713
714 case VisibilityNotify:
715 handleVisibility(event.xvisibility);
716 break;
717
718 default:
719 if (damage.isEvent(event.type, XDamageNotify)) {
720 handleDamageNotify(
721 *reinterpret_cast<const XDamageNotifyEvent *>(&event));
722 break;
723 }
724 #ifdef CONFIG_SHAPE
725 if (shapes.isEvent(event.type, ShapeNotify)) {
726 handleShapeNotify(*reinterpret_cast<const XShapeEvent *>(&event));
727 break;
728 }
729 #endif
730 #ifdef CONFIG_XRANDR
731 if (xrandr.isEvent(event.type, RRScreenChangeNotify)) {
732 handleRRScreenChangeNotify(
733 reinterpret_cast<const XRRScreenChangeNotifyEvent&>(event));
734 break;
735 }
736 else if (xrandr.isEvent(event.type, RRNotify)) {
737 handleRRNotify(reinterpret_cast<const XRRNotifyEvent&>(event));
738 break;
739 }
740 #endif
741 break;
742 }
743 }
744
745 /// TODO #warning "implement expose compression"
paintExpose(int ex,int ey,int ew,int eh)746 void YWindow::paintExpose(int ex, int ey, int ew, int eh) {
747 if (ex < 0) {
748 ew += ex;
749 ex = 0;
750 }
751 if (ey < 0) {
752 eh += ey;
753 ey = 0;
754 }
755 ew = min(ew, int(width()) - ex);
756 eh = min(eh, int(height()) - ey);
757 if (ew > 0 && eh > 0) {
758 Graphics& g(getGraphics());
759 XRectangle r = {
760 short(ex),
761 short(ey),
762 static_cast<unsigned short>(ew),
763 static_cast<unsigned short>(eh),
764 };
765 g.setClipRectangles(&r, 1);
766 paint(g, r);
767 g.resetClip();
768 }
769 }
770
handleExpose(const XExposeEvent & expose)771 void YWindow::handleExpose(const XExposeEvent &expose) {
772 paintExpose(expose.x, expose.y, expose.width, expose.height);
773 }
774
handleGraphicsExpose(const XGraphicsExposeEvent & expose)775 void YWindow::handleGraphicsExpose(const XGraphicsExposeEvent &expose) {
776 paintExpose(expose.x, expose.y, expose.width, expose.height);
777 }
778
handleConfigure(const XConfigureEvent & configure)779 void YWindow::handleConfigure(const XConfigureEvent &configure) {
780 if (configure.window == handle()) {
781 YRect evt(configure.x, configure.y, configure.width, configure.height);
782 YRect old(geometry());
783 if (evt != old)
784 {
785 fX = configure.x;
786 fY = configure.y;
787 fWidth = configure.width;
788 fHeight = configure.height;
789
790 this->configure(YRect2(evt, old));
791 }
792 }
793 }
794
handleGravityNotify(const XGravityEvent & gravity)795 void YWindow::handleGravityNotify(const XGravityEvent& gravity) {
796 }
797
handleKey(const XKeyEvent & key)798 bool YWindow::handleKey(const XKeyEvent &key) {
799 if (key.type == KeyPress) {
800 KeySym k = keyCodeToKeySym(key.keycode);
801 unsigned int m = KEY_MODMASK(key.state);
802
803 if (accel) {
804 YAccelerator *a;
805
806 for (a = accel; a; a = a->next) {
807 //msg("%c %d - %c %d %d", k, k, a->key, a->key, a->mod);
808 if (m == a->mod && k == a->key)
809 if (a->win->handleKey(key))
810 return true;
811 }
812 if (ASCII::isLower(char(k))) {
813 k = ASCII::toUpper(char(k));
814 for (a = accel; a; a = a->next)
815 if (m == a->mod && k == a->key)
816 if (a->win->handleKey(key))
817 return true;
818 }
819 }
820 if (k == XK_Tab) {
821 if (m == 0) {
822 nextFocus();
823 return true;
824 }
825 else if (m == ShiftMask) {
826 prevFocus();
827 return true;
828 }
829 }
830 }
831 return false;
832 }
833
handleButton(const XButtonEvent & button)834 void YWindow::handleButton(const XButtonEvent &button) {
835 if (fToolTip) {
836 fToolTip->leave();
837 }
838
839 int const dx(abs(button.x_root - fClickEvent.x_root));
840 int const dy(abs(button.y_root - fClickEvent.y_root));
841 int const motionDelta(max(dx, dy));
842
843 if (button.type == ButtonPress) {
844 fClickDrag = false;
845
846 if (fClickWindow != this) {
847 fClickWindow = this;
848 fClickCount = 1;
849 } else {
850 if (button.time < fClickTime + MultiClickTime &&
851 fClickButton == button.button &&
852 motionDelta <= ClickMotionDistance &&
853 button.x >= 0 && button.y >= 0 &&
854 button.x < int(width()) && button.y < int(height()))
855 {
856 fClickCount++;
857 handleClickDown(button, fClickCount);
858 } else
859 fClickCount = 1;
860 }
861 fClickEvent = button;
862 fClickButton = fClickButtonDown = button.button;
863 fClickTime = button.time;
864 }
865 else if (button.type == ButtonRelease) {
866 if ((fClickWindow == this) &&
867 !fClickDrag &&
868 fClickCount > 0 &&
869 fClickButtonDown == button.button &&
870 // motionDelta <= ClickMotionDistance &&
871 button.x >= 0 && button.y >= 0 &&
872 button.x < int(width()) && button.y < int(height()))
873 {
874 fClickButtonDown = 0;
875 handleClick(button, fClickCount);
876 } else {
877 fClickWindow = nullptr;
878 fClickCount = 1;
879 fClickButtonDown = 0;
880 fClickButton = 0;
881 if (fClickDrag) {
882 handleEndDrag(fClickEvent, button);
883 fClickDrag = false;
884 }
885 }
886 }
887 }
888
handleMotion(const XMotionEvent & motion)889 void YWindow::handleMotion(const XMotionEvent &motion) {
890 if (fClickButtonDown) {
891 if (fClickDrag) {
892 handleDrag(fClickEvent, motion);
893 } else {
894 int const dx(abs(motion.x_root - fClickEvent.x_root));
895 int const dy(abs(motion.y_root - fClickEvent.y_root));
896 int const motionDelta(max(dx, dy));
897 if (motion.time > fClickTime + ClickMotionDelay ||
898 motionDelta >= ClickMotionDistance)
899 {
900 if ((motion.state & xapp->ButtonMask) / Button1Mask
901 == fClickButton)
902 {
903 fClickDrag = handleBeginDrag(fClickEvent, motion);
904 }
905 }
906 }
907 }
908 }
909
handleBeginDrag(const XButtonEvent &,const XMotionEvent &)910 bool YWindow::handleBeginDrag(const XButtonEvent &, const XMotionEvent &) {
911 return false;
912 }
913
setToolTip(const mstring & tip)914 void YWindow::setToolTip(const mstring& tip) {
915 if (tip == null) {
916 fToolTip = null;
917 } else {
918 fToolTip->setText(tip);
919 }
920 }
921
getToolTip()922 mstring YWindow::getToolTip() {
923 return fToolTip ? fToolTip->getText() : null;
924 }
925
toolTipVisible()926 bool YWindow::toolTipVisible() {
927 return fToolTip && fToolTip->visible();
928 }
929
toolTipVisibility(bool visible)930 void YWindow::toolTipVisibility(bool visible) {
931 visible ? fToolTip->enter(this) : fToolTip->leave();
932 }
933
updateToolTip()934 void YWindow::updateToolTip() {
935 }
936
handleCrossing(const XCrossingEvent & crossing)937 void YWindow::handleCrossing(const XCrossingEvent& crossing) {
938 if (fToolTip || fStyle & wsToolTipping) {
939 if (crossing.type == EnterNotify && crossing.mode == NotifyNormal) {
940 updateToolTip();
941 if (fToolTip) {
942 fToolTip->enter(this);
943 }
944 }
945 else if (crossing.type == LeaveNotify) {
946 if (fToolTip) {
947 fToolTip->leave();
948 }
949 }
950 }
951 }
952
handleClientMessage(const XClientMessageEvent & message)953 void YWindow::handleClientMessage(const XClientMessageEvent& message) {
954 if (message.message_type == _XA_WM_PROTOCOLS
955 && message.format == 32
956 && message.data.l[0] == long(_XA_WM_DELETE_WINDOW))
957 {
958 handleClose();
959 }
960 else if (message.message_type == _XA_WM_PROTOCOLS
961 && message.format == 32
962 && message.data.l[0] == long(_XA_WM_TAKE_FOCUS))
963 {
964 gotFocus();
965 #if 0
966 YWindow *w = getFocusWindow();
967 if (w)
968 w->gotFocus();
969 else
970 gotFocus();
971 #endif
972 }
973 }
974
handleClientMessage(const XClientMessageEvent & message)975 void YDndWindow::handleClientMessage(const XClientMessageEvent& message) {
976 if (message.message_type == XA_XdndEnter) {
977 handleXdndEnter(message);
978 }
979 else if (message.message_type == XA_XdndLeave) {
980 handleXdndLeave(message);
981 }
982 else if (message.message_type == XA_XdndPosition) {
983 handleXdndPosition(message);
984 }
985 else if (message.message_type == XA_XdndStatus) {
986 handleXdndStatus(message);
987 }
988 else if (message.message_type == XA_XdndDrop) {
989 handleXdndDrop(message);
990 }
991 else if (message.message_type == XA_XdndFinished) {
992 handleXdndFinished(message);
993 }
994 else {
995 YWindow::handleClientMessage(message);
996 }
997 }
998
handleVisibility(const XVisibilityEvent & visibility)999 void YWindow::handleVisibility(const XVisibilityEvent& visibility) {
1000 }
1001
handleMapNotify(const XMapEvent &)1002 void YWindow::handleMapNotify(const XMapEvent &) {
1003 // ignore "map notify" not implemented or needed due to MapRequest event
1004 }
1005
handleUnmapNotify(const XUnmapEvent & xunmap)1006 void YWindow::handleUnmapNotify(const XUnmapEvent &xunmap) {
1007 if (xunmap.window == xunmap.event || xunmap.send_event) {
1008 if (ignoreUnmap() == false) {
1009 flags &= unsigned(~wfVisible);
1010 handleUnmap(xunmap);
1011 }
1012 }
1013 }
1014
handleUnmap(const XUnmapEvent &)1015 void YWindow::handleUnmap(const XUnmapEvent &) {
1016 }
1017
handleConfigureRequest(const XConfigureRequestEvent &)1018 void YWindow::handleConfigureRequest(const XConfigureRequestEvent&) {
1019 }
1020
handleMapRequest(const XMapRequestEvent &)1021 void YWindow::handleMapRequest(const XMapRequestEvent&) {
1022 }
1023
handleDestroyWindow(const XDestroyWindowEvent & destroyWindow)1024 void YWindow::handleDestroyWindow(const XDestroyWindowEvent &destroyWindow) {
1025 if (destroyWindow.window == fHandle) {
1026 setDestroyed();
1027 removeAllIgnoreUnmap();
1028 }
1029 }
1030
paint(Graphics & g,const YRect & r)1031 void YWindow::paint(Graphics &g, const YRect &r) {
1032 g.fillRect(r.x(), r.y(), r.width(), r.height());
1033 }
1034
nullGeometry()1035 bool YWindow::nullGeometry() {
1036 bool zero = (fWidth == 0 || fHeight == 0);
1037
1038 if (zero && !(flags & wfNullSize)) {
1039 flags |= wfNullSize;
1040 if (flags & wfVisible) {
1041 addIgnoreUnmap();
1042 XUnmapWindow(xapp->display(), handle());
1043 }
1044 } else if ((flags & wfNullSize) && !zero) {
1045 flags &= unsigned(~wfNullSize);
1046 if (flags & wfVisible)
1047 XMapWindow(xapp->display(), handle());
1048 }
1049 return zero;
1050 }
1051
setGeometry(const YRect & r)1052 void YWindow::setGeometry(const YRect &r) {
1053 YRect old(geometry());
1054
1055 if (r != old) {
1056 fX = r.x();
1057 fY = r.y();
1058 fWidth = r.width();
1059 fHeight = r.height();
1060
1061 if (flags & wfCreated) {
1062 if (!nullGeometry())
1063 XMoveResizeWindow(xapp->display(),
1064 fHandle,
1065 fX, fY, fWidth, fHeight);
1066 }
1067
1068 configure(YRect2(r, old));
1069 }
1070 }
1071
setPosition(int x,int y)1072 void YWindow::setPosition(int x, int y) {
1073 if (x != fX || y != fY) {
1074 YRect old(geometry());
1075
1076 fX = x;
1077 fY = y;
1078
1079 if (flags & wfCreated)
1080 XMoveWindow(xapp->display(), fHandle, fX, fY);
1081
1082 configure(YRect2(geometry(), old));
1083 }
1084 }
1085
setSize(unsigned width,unsigned height)1086 void YWindow::setSize(unsigned width, unsigned height) {
1087 if (width != fWidth || height != fHeight) {
1088 YRect old(geometry());
1089
1090 fWidth = width;
1091 fHeight = height;
1092
1093 if (flags & wfCreated)
1094 if (!nullGeometry())
1095 XResizeWindow(xapp->display(), fHandle, fWidth, fHeight);
1096
1097 configure(YRect2(geometry(), old));
1098 }
1099 }
1100
setBorderWidth(unsigned width)1101 void YWindow::setBorderWidth(unsigned width) {
1102 XSetWindowBorderWidth(xapp->display(), handle(), width);
1103 }
1104
setBackground(unsigned long pixel)1105 void YWindow::setBackground(unsigned long pixel) {
1106 XSetWindowBackground(xapp->display(), handle(), pixel);
1107 }
1108
setBackgroundPixmap(Pixmap pixmap)1109 void YWindow::setBackgroundPixmap(Pixmap pixmap) {
1110 XSetWindowBackgroundPixmap(xapp->display(), handle(), pixmap);
1111 }
1112
setBackgroundPixmap(ref<YPixmap> pixmap)1113 void YWindow::setBackgroundPixmap(ref<YPixmap> pixmap) {
1114 setBackgroundPixmap(pixmap->pixmap(depth()));
1115 }
1116
setParentRelative()1117 void YWindow::setParentRelative() {
1118 setBackgroundPixmap(ParentRelative);
1119 }
1120
mapToGlobal(int & x,int & y)1121 void YWindow::mapToGlobal(int& x, int& y) {
1122 Window child;
1123
1124 XTranslateCoordinates(xapp->display(),
1125 handle(),
1126 desktop->handle(),
1127 x, y,
1128 &x, &y, &child);
1129 }
1130
mapToLocal(int & x,int & y)1131 void YWindow::mapToLocal(int& x, int& y) {
1132 Window child;
1133
1134 XTranslateCoordinates(xapp->display(),
1135 desktop->handle(),
1136 handle(),
1137 x, y,
1138 &x, &y, &child);
1139 }
1140
configure(const YRect &)1141 void YWindow::configure(const YRect &/*r*/)
1142 {
1143 }
1144
configure(const YRect2 & r2)1145 void YWindow::configure(const YRect2& r2)
1146 {
1147 configure((const YRect &) r2);
1148 }
1149
setPointer(Cursor pointer)1150 void YWindow::setPointer(Cursor pointer) {
1151 fPointer = pointer;
1152
1153 if (flags & wfCreated) {
1154 XSetWindowAttributes attributes;
1155 attributes.cursor = fPointer;
1156 XChangeWindowAttributes(xapp->display(), handle(),
1157 CWCursor, &attributes);
1158 }
1159 }
1160
grabKeyM(int keycode,unsigned int modifiers)1161 void YWindow::grabKeyM(int keycode, unsigned int modifiers) {
1162 MSG(("grabKey %d %d %s", keycode, modifiers,
1163 XKeysymToString(keyCodeToKeySym(keycode))));
1164
1165 XGrabKey(xapp->display(), keycode, modifiers, handle(), False,
1166 GrabModeAsync, GrabModeAsync);
1167 }
1168
grabKey(int key,unsigned int modifiers)1169 void YWindow::grabKey(int key, unsigned int modifiers) {
1170 KeyCode keycode = XKeysymToKeycode(xapp->display(), KeySym(key));
1171 if (keycode != 0) {
1172 grabKeyM(keycode, modifiers);
1173 if (modifiers != AnyModifier) {
1174 grabKeyM(keycode, modifiers | LockMask);
1175 if (xapp->NumLockMask != 0) {
1176 grabKeyM(keycode, modifiers | xapp->NumLockMask);
1177 grabKeyM(keycode, modifiers | xapp->NumLockMask | LockMask);
1178 }
1179 }
1180 }
1181 }
1182
grabButtonM(int button,unsigned int modifiers)1183 void YWindow::grabButtonM(int button, unsigned int modifiers) {
1184 XGrabButton(xapp->display(), unsigned(button), modifiers,
1185 handle(), True, ButtonPressMask,
1186 GrabModeAsync, GrabModeAsync, None, None);
1187 }
1188
grabButton(int button,unsigned int modifiers)1189 void YWindow::grabButton(int button, unsigned int modifiers) {
1190 grabButtonM(button, modifiers);
1191 if (modifiers != AnyModifier) {
1192 grabButtonM(button, modifiers | LockMask);
1193 if (xapp->NumLockMask != 0) {
1194 grabButtonM(button, modifiers | xapp->NumLockMask);
1195 grabButtonM(button, modifiers | xapp->NumLockMask | LockMask);
1196 }
1197 }
1198 }
1199
captureEvents()1200 void YWindow::captureEvents() {
1201 xapp->captureGrabEvents(this);
1202 }
1203
releaseEvents()1204 void YWindow::releaseEvents() {
1205 xapp->releaseGrabEvents(this);
1206 }
1207
donePopup(YPopupWindow *)1208 void YWindow::donePopup(YPopupWindow * /*command*/) {
1209 }
1210
handleClose()1211 void YWindow::handleClose() {
1212 }
1213
handleFocus(const XFocusChangeEvent & xfocus)1214 void YWindow::handleFocus(const XFocusChangeEvent &xfocus) {
1215 if (isToplevel()) {
1216 if (xfocus.type == FocusIn) {
1217 gotFocus();
1218 } else if (xfocus.type == FocusOut) {
1219 lostFocus();
1220 }
1221 }
1222 }
1223
isFocusTraversable()1224 bool YWindow::isFocusTraversable() {
1225 return false;
1226 }
1227
requestFocus(bool requestUserFocus)1228 void YWindow::requestFocus(bool requestUserFocus) {
1229 if (isToplevel()) {
1230 if (visible() && requestUserFocus)
1231 setWindowFocus();
1232 } else {
1233 if (parent()) {
1234 parent()->requestFocus(requestUserFocus);
1235 parent()->setFocus(this);
1236 }
1237 }
1238 }
1239
1240
toplevel()1241 YWindow *YWindow::toplevel() {
1242 for (YWindow *w = this; w; w = w->fParentWindow) {
1243 if (w->isToplevel())
1244 return w;
1245 }
1246 return nullptr;
1247 }
1248
nextFocus()1249 void YWindow::nextFocus() {
1250 YWindow *t = toplevel();
1251
1252 if (t)
1253 t->changeFocus(true);
1254 }
1255
prevFocus()1256 void YWindow::prevFocus() {
1257 YWindow *t = toplevel();
1258
1259 if (t)
1260 t->changeFocus(false);
1261 }
1262
getFocusWindow()1263 YWindow *YWindow::getFocusWindow() {
1264 YWindow *w = this;
1265 while (w->fFocusedWindow) {
1266 w = w->fFocusedWindow;
1267 }
1268 return w;
1269 }
1270
changeFocus(bool next)1271 bool YWindow::changeFocus(bool next) {
1272 YWindow *cur = getFocusWindow();
1273
1274 if (cur == nullptr) {
1275 if (next)
1276 cur = lastWindow();
1277 else
1278 cur = firstWindow();
1279 }
1280
1281 YWindow *org = cur;
1282 if (cur) do {
1283 ///!!! need focus ordering
1284
1285 if (next) {
1286 if (cur->lastWindow())
1287 cur = cur->lastWindow();
1288 else if (cur->prevWindow())
1289 cur = cur->prevWindow();
1290 else if (cur->isToplevel())
1291 {}
1292 else {
1293 while (cur->parent()) {
1294 cur = cur->parent();
1295 if (cur->isToplevel())
1296 break;
1297 if (cur->prevWindow()) {
1298 cur = cur->prevWindow();
1299 break;
1300 }
1301 }
1302 }
1303 } else {
1304 // is reverse tabbing of nested windows correct?
1305 if (cur->firstWindow())
1306 cur = cur->firstWindow();
1307 else if (cur->nextWindow())
1308 cur = cur->nextWindow();
1309 else if (cur->isToplevel())
1310 /**/;
1311 else {
1312 while (cur->parent()) {
1313 cur = cur->parent();
1314 if (cur->isToplevel())
1315 break;
1316 if (cur->nextWindow()) {
1317 cur = cur->nextWindow();
1318 break;
1319 }
1320 }
1321 }
1322 }
1323
1324 if (cur->isFocusTraversable()) {
1325 cur->requestFocus(false);
1326 return true;
1327 }
1328 } while (cur != org);
1329
1330 return false;
1331 }
1332
setFocus(YWindow * window)1333 void YWindow::setFocus(YWindow *window) {
1334 if (window != fFocusedWindow) {
1335 YWindow *oldFocus = fFocusedWindow;
1336
1337 fFocusedWindow = window;
1338
1339 if (focused()) {
1340 if (oldFocus)
1341 oldFocus->lostFocus();
1342 if (fFocusedWindow)
1343 fFocusedWindow->gotFocus();
1344 }
1345 }
1346 }
gotFocus()1347 void YWindow::gotFocus() {
1348 if (parent() == nullptr || isToplevel() || parent()->focused()) {
1349 if (!(flags & wfFocused)) {
1350 flags |= wfFocused;
1351 repaintFocus();
1352 if (fFocusedWindow)
1353 fFocusedWindow->gotFocus();
1354 }
1355 }
1356 }
1357
lostFocus()1358 void YWindow::lostFocus() {
1359 if (flags & wfFocused) {
1360 if (fFocusedWindow)
1361 fFocusedWindow->lostFocus();
1362 flags &= unsigned(~wfFocused);
1363 repaintFocus();
1364 }
1365 }
1366
installAccelerator(unsigned int key,unsigned int mod,YWindow * win)1367 void YWindow::installAccelerator(unsigned int key, unsigned int mod, YWindow *win) {
1368 if (key < 128)
1369 key = ASCII::toUpper(char(key));
1370 if (isToplevel() || fParentWindow == nullptr) {
1371 YAccelerator **pa = &accel, *a;
1372
1373 while (*pa) {
1374 a = *pa;
1375 if (a->key == key &&
1376 a->mod == mod &&
1377 a->win == win)
1378 {
1379 assert(1 == 0);
1380 return ;
1381 } else
1382 pa = &(a->next);
1383 }
1384
1385 a = new YAccelerator;
1386 if (a == nullptr)
1387 return ;
1388
1389 a->key = key;
1390 a->mod = mod;
1391 a->win = win;
1392 a->next = accel;
1393 accel = a;
1394 } else parent()->installAccelerator(key, mod, win);
1395 }
1396
removeAccelerator(unsigned int key,unsigned int mod,YWindow * win)1397 void YWindow::removeAccelerator(unsigned int key, unsigned int mod, YWindow *win) {
1398 if (key < 128)
1399 key = ASCII::toUpper(char(key));
1400 if (isToplevel() || fParentWindow == nullptr) {
1401 YAccelerator **pa = &accel, *a;
1402
1403 while (*pa) {
1404 a = *pa;
1405 if (a->key == key &&
1406 a->mod == mod &&
1407 a->win == win)
1408 {
1409 *pa = a->next;
1410 delete a;
1411 } else
1412 pa = &(a->next);
1413 }
1414 } else parent()->removeAccelerator(key, mod, win);
1415 }
1416
deleteProperty(Atom property)1417 void YWindow::deleteProperty(Atom property) {
1418 XDeleteProperty(xapp->display(), handle(), property);
1419 }
1420
setProperty(Atom prop,Atom type,const Atom * values,int count)1421 void YWindow::setProperty(Atom prop, Atom type, const Atom* values, int count) {
1422 XChangeProperty(xapp->display(), handle(), prop, type, 32, PropModeReplace,
1423 reinterpret_cast<const unsigned char *>(values), count);
1424 }
1425
setProperty(Atom property,Atom propType,Atom value)1426 void YWindow::setProperty(Atom property, Atom propType, Atom value) {
1427 setProperty(property, propType, &value, 1);
1428 }
1429
setNetName(const char * name)1430 void YWindow::setNetName(const char* name) {
1431 int length = int(strlen(name));
1432 XChangeProperty(xapp->display(), handle(), _XA_NET_WM_NAME, _XA_UTF8_STRING,
1433 8, PropModeReplace, (const unsigned char *) name, length);
1434 }
1435
setNetWindowType(Atom window_type)1436 void YWindow::setNetWindowType(Atom window_type) {
1437 setProperty(_XA_NET_WM_WINDOW_TYPE, XA_ATOM, window_type);
1438 }
1439
setNetOpacity(Atom opacity)1440 void YWindow::setNetOpacity(Atom opacity) {
1441 setProperty(_XA_NET_WM_WINDOW_OPACITY, XA_CARDINAL, opacity);
1442 }
1443
setNetPid()1444 void YWindow::setNetPid() {
1445 setProperty(_XA_NET_WM_PID, XA_CARDINAL, getpid());
1446 }
1447
setToplevel(bool enabled)1448 void YWindow::setToplevel(bool enabled) {
1449 if (isToplevel() != enabled) {
1450 flags = enabled ? (flags | wfToplevel) : (flags &~ wfToplevel);
1451 }
1452 }
1453
YDndWindow(YWindow * parent,Window win,int depth,Visual * visual,Colormap colormap)1454 YDndWindow::YDndWindow(YWindow* parent, Window win, int depth,
1455 Visual* visual, Colormap colormap) :
1456 YWindow(parent, win, depth, visual, colormap),
1457 XdndDragSource(None),
1458 XdndDropTarget(None)
1459 {
1460 }
1461
setDND(bool enabled)1462 void YDndWindow::setDND(bool enabled) {
1463 if (enabled) {
1464 setProperty(XA_XdndAware, XA_ATOM, XdndCurrentVersion);
1465 } else {
1466 deleteProperty(XA_XdndAware);
1467 }
1468 }
1469
sendXdndStatus(bool acceptDrop,Atom dropAction)1470 void YDndWindow::sendXdndStatus(bool acceptDrop, Atom dropAction) {
1471 if (XdndDragSource) {
1472 int x_root = 0, y_root = 0;
1473 mapToGlobal(x_root, y_root);
1474 XClientMessageEvent msg = { ClientMessage, None, False, nullptr, };
1475 msg.window = XdndDragSource;
1476 msg.message_type = XA_XdndStatus;
1477 msg.format = 32;
1478 msg.data.l[0] = long(handle());
1479 msg.data.l[1] = (2 | acceptDrop);
1480 msg.data.l[2] = (x_root << 16) | (y_root & 0xFFFF);
1481 msg.data.l[3] = (width() << 16) | (height() & 0xFFFF);
1482 msg.data.l[4] = long(dropAction);
1483 xapp->send(msg, XdndDragSource);
1484 }
1485 }
1486
handleXdndEnter(const XClientMessageEvent & message)1487 void YDndWindow::handleXdndEnter(const XClientMessageEvent& message) {
1488 if (message.message_type == XA_XdndEnter) {
1489 const long* data = message.data.l;
1490 long version = ((data[1] >> 24) & 0xF);
1491 MSG(("XdndEnter source=0x%lX version=%ld %ld %ld %ld",
1492 data[0], version, data[2], data[3], data[4]));
1493 if (version <= XdndCurrentVersion) {
1494 XdndDragSource = static_cast<Window>(data[0]);
1495 } else {
1496 XdndDragSource = None;
1497 }
1498 XdndDropTarget = None;
1499 }
1500 }
1501
handleXdndLeave(const XClientMessageEvent & message)1502 void YDndWindow::handleXdndLeave(const XClientMessageEvent& message) {
1503 if (message.message_type == XA_XdndLeave) {
1504 Window source = static_cast<Window>(message.data.l[0]);
1505 MSG(("XdndLeave source=0x%lX", source));
1506 if (source == XdndDragSource && XdndDragSource) {
1507 if (XdndDropTarget) {
1508 YWindow* win = windowContext.find(XdndDropTarget);
1509 if (win)
1510 win->handleDNDLeave();
1511 XdndDropTarget = None;
1512 }
1513 XdndDragSource = None;
1514 }
1515 }
1516 }
1517
handleXdndPosition(const XClientMessageEvent & message)1518 void YDndWindow::handleXdndPosition(const XClientMessageEvent& message) {
1519 if (message.message_type == XA_XdndPosition && XdndDragSource) {
1520 const long* data = message.data.l;
1521 XdndDragSource = static_cast<Window>(data[0]);
1522 int x = short(data[2] >> 16);
1523 int y = short(data[2] & 0xFFFF);
1524
1525 MSG(("XdndPosition 0x%lX %s 0x%lX %d:%d",
1526 data[0], xapp->atomName(data[4]), handle(), x, y));
1527
1528 Window target = handle(), child = None;
1529 int nx, ny;
1530 do {
1531 if (XTranslateCoordinates(xapp->display(), desktop->handle(),
1532 target, x, y, &nx, &ny, &child)) {
1533 if (child) {
1534 target = child;
1535 }
1536 } else {
1537 target = None;
1538 break;
1539 }
1540 } while (child);
1541
1542 YWindow* pwin = nullptr;
1543 if (target != XdndDropTarget) {
1544 if (XdndDropTarget) {
1545 YWindow* ptr = windowContext.find(XdndDropTarget);
1546 if (ptr)
1547 ptr->handleDNDLeave();
1548 }
1549 XdndDropTarget = target;
1550 if (XdndDropTarget) {
1551 YWindow* ptr = windowContext.find(XdndDropTarget);
1552 if (ptr) {
1553 ptr->handleDNDEnter();
1554 pwin = ptr;
1555 }
1556 }
1557 }
1558 if (pwin == nullptr && XdndDropTarget) {
1559 pwin = windowContext.find(XdndDropTarget);
1560 }
1561 if (pwin)
1562 pwin->handleDNDPosition(nx, ny);
1563 MSG(("XdndPosition 0x%lX %s 0x%lX %d:%d",
1564 data[0], xapp->atomName(data[4]), XdndDropTarget, nx, ny));
1565 sendXdndStatus();
1566 }
1567 }
1568
handleXdndStatus(const XClientMessageEvent & message)1569 void YDndWindow::handleXdndStatus(const XClientMessageEvent& message) {
1570 if (message.message_type == XA_XdndStatus) {
1571 MSG(("XdndStatus target=0x%lX accept=%s",
1572 message.data.l[0], boolstr(message.data.l[1] & 1)));
1573 }
1574 }
1575
handleXdndDrop(const XClientMessageEvent & message)1576 void YDndWindow::handleXdndDrop(const XClientMessageEvent& message) {
1577 if (message.message_type == XA_XdndDrop) {
1578 MSG(("XdndDrop source=0x%lx time=%ld.%03ld", message.data.l[0],
1579 message.data.l[2] / 1000, message.data.l[2] % 1000));
1580 }
1581 }
1582
handleXdndFinished(const XClientMessageEvent & message)1583 void YDndWindow::handleXdndFinished(const XClientMessageEvent& message) {
1584 if (message.message_type == XA_XdndFinished) {
1585 MSG(("XdndFinished target=0x%lX accept=%s action=%s",
1586 message.data.l[0], boolstr(message.data.l[1] & 1),
1587 xapp->atomName(message.data.l[2])));
1588 }
1589 }
1590
handleDNDEnter()1591 void YWindow::handleDNDEnter() {
1592 }
1593
handleDNDLeave()1594 void YWindow::handleDNDLeave() {
1595 }
1596
handleDNDPosition(int,int)1597 void YWindow::handleDNDPosition(int /*x*/, int /*y*/) {
1598 }
1599
handleAutoScroll(const XMotionEvent &)1600 bool YWindow::handleAutoScroll(const XMotionEvent & /*mouse*/) {
1601 return false;
1602 }
1603
beginAutoScroll(bool doScroll,const XMotionEvent * motion)1604 void YWindow::beginAutoScroll(bool doScroll, const XMotionEvent *motion) {
1605 if (fAutoScroll == nullptr)
1606 fAutoScroll = new YAutoScroll();
1607 if (fAutoScroll)
1608 fAutoScroll->autoScroll(this, doScroll, motion);
1609 }
1610
handleSelectionClear(const XSelectionClearEvent &)1611 void YWindow::handleSelectionClear(const XSelectionClearEvent &/*clear*/) {
1612 }
1613
handleSelectionRequest(const XSelectionRequestEvent &)1614 void YWindow::handleSelectionRequest(const XSelectionRequestEvent &/*request*/) {
1615 }
1616
handleSelection(const XSelectionEvent &)1617 void YWindow::handleSelection(const XSelectionEvent &/*selection*/) {
1618 }
1619
acquireSelection(bool selection)1620 void YWindow::acquireSelection(bool selection) {
1621 Atom sel = selection ? XA_PRIMARY : _XA_CLIPBOARD;
1622
1623 XSetSelectionOwner(xapp->display(), sel, handle(),
1624 xapp->getEventTime("acquireSelection"));
1625 }
1626
clearSelection(bool selection)1627 void YWindow::clearSelection(bool selection) {
1628 Atom sel = selection ? XA_PRIMARY : _XA_CLIPBOARD;
1629
1630 XSetSelectionOwner(xapp->display(), sel, None,
1631 xapp->getEventTime("clearSelection"));
1632 }
1633
requestSelection(bool selection)1634 void YWindow::requestSelection(bool selection) {
1635 Atom sel = selection ? XA_PRIMARY : _XA_CLIPBOARD;
1636
1637 XConvertSelection(xapp->display(),
1638 sel, _XA_UTF8_STRING,
1639 sel, handle(), xapp->getEventTime("requestSelection"));
1640 }
1641
hasPopup()1642 bool YWindow::hasPopup() {
1643 YPopupWindow *p = xapp->popup();
1644 if (p) {
1645 while (p->prevPopup()) {
1646 p = p->prevPopup();
1647 }
1648 for (YWindow *w = p->popupOwner(); w; w = w->parent()) {
1649 if (w == this)
1650 return true;
1651 }
1652 }
1653 return false;
1654 }
1655
YDesktop(YWindow * aParent,Window win)1656 YDesktop::YDesktop(YWindow *aParent, Window win):
1657 YWindow(aParent, win)
1658 {
1659 desktop = this;
1660 unsigned w = 0, h = 0;
1661 updateXineramaInfo(w, h);
1662 }
1663
~YDesktop()1664 YDesktop::~YDesktop() {
1665 #if DEBUG || PRECON
1666 for (YWindow* w; (w = firstWindow()) != nullptr; delete w) {
1667 char* name = demangle(typeid(*w).name());
1668 INFO("deleting stray %s", name);
1669 free(name);
1670 }
1671 #endif
1672 if (desktop == this)
1673 desktop = nullptr;
1674 }
1675
grabVKey(int key,unsigned int vm)1676 void YWindow::grabVKey(int key, unsigned int vm) {
1677 unsigned m = 0;
1678
1679 if (vm & kfShift)
1680 m |= ShiftMask;
1681 if (vm & kfCtrl)
1682 m |= ControlMask;
1683 if (vm & kfAlt)
1684 m |= xapp->AltMask;
1685 if (vm & kfMeta)
1686 m |= xapp->MetaMask;
1687 if (vm & kfSuper)
1688 m |= xapp->SuperMask;
1689 if (vm & kfHyper)
1690 m |= xapp->HyperMask;
1691 if (vm & kfAltGr)
1692 m |= xapp->ModeSwitchMask;
1693
1694 MSG(("grabVKey %d %d %d", key, vm, m));
1695 if (key != 0 && (vm == 0 || m != 0)) {
1696 if ((!(vm & kfMeta) || xapp->MetaMask) &&
1697 (!(vm & kfAlt) || xapp->AltMask) &&
1698 (!(vm & kfSuper) || xapp->SuperMask) &&
1699 (!(vm & kfHyper) || xapp->HyperMask) &&
1700 (!(vm & kfAltGr) || xapp->ModeSwitchMask))
1701 {
1702 grabKey(key, m);
1703 }
1704
1705 // !!! recheck this
1706 if (((vm & (kfAlt | kfCtrl)) == (kfAlt | kfCtrl)) &&
1707 modSuperIsCtrlAlt &&
1708 xapp->WinMask)
1709 {
1710 m = xapp->WinMask;
1711 if (vm & kfShift)
1712 m |= ShiftMask;
1713 if (vm & kfSuper)
1714 m |= xapp->SuperMask;
1715 if (vm & kfHyper)
1716 m |= xapp->HyperMask;
1717 if (vm & kfAltGr)
1718 m |= xapp->ModeSwitchMask;
1719 grabKey(key, m);
1720 }
1721 }
1722 }
1723
grabVButton(int button,unsigned int vm)1724 void YWindow::grabVButton(int button, unsigned int vm) {
1725 unsigned m = 0;
1726
1727 if (vm & kfShift)
1728 m |= ShiftMask;
1729 if (vm & kfCtrl)
1730 m |= ControlMask;
1731 if (vm & kfAlt)
1732 m |= xapp->AltMask;
1733 if (vm & kfMeta)
1734 m |= xapp->MetaMask;
1735 if (vm & kfSuper)
1736 m |= xapp->SuperMask;
1737 if (vm & kfHyper)
1738 m |= xapp->HyperMask;
1739 if (vm & kfAltGr)
1740 m |= xapp->ModeSwitchMask;
1741
1742 MSG(("grabVButton %d %d %d", button, vm, m));
1743
1744 if (button != 0 && (vm == 0 || m != 0)) {
1745 if ((!(vm & kfMeta) || xapp->MetaMask) &&
1746 (!(vm & kfAlt) || xapp->AltMask) &&
1747 (!(vm & kfSuper) || xapp->SuperMask) &&
1748 (!(vm & kfHyper) || xapp->HyperMask) &&
1749 (!(vm & kfAltGr) || xapp->ModeSwitchMask))
1750 {
1751 grabButton(button, m);
1752 }
1753
1754 // !!! recheck this
1755 if (((vm & (kfAlt | kfCtrl)) == (kfAlt | kfCtrl)) &&
1756 modSuperIsCtrlAlt &&
1757 xapp->WinMask)
1758 {
1759 m = xapp->WinMask;
1760 if (vm & kfShift)
1761 m |= ShiftMask;
1762 if (vm & kfSuper)
1763 m |= xapp->SuperMask;
1764 if (vm & kfHyper)
1765 m |= xapp->HyperMask;
1766 if (vm & kfAltGr)
1767 m |= xapp->ModeSwitchMask;
1768 grabButton(button, m);
1769 }
1770 }
1771 }
1772
VMod(unsigned m)1773 unsigned YWindow::VMod(unsigned m) {
1774 unsigned vm = 0;
1775 unsigned m1 = m & ~xapp->WinMask;
1776
1777 if (m & xapp->WinMask) {
1778 if (modSuperIsCtrlAlt) {
1779 vm |= kfCtrl + kfAlt;
1780 } else if (xapp->WinMask == xapp->SuperMask) {
1781 vm |= kfSuper;
1782 }
1783 }
1784
1785 if (m1 & ShiftMask)
1786 vm |= kfShift;
1787 if (m1 & ControlMask)
1788 vm |= kfCtrl;
1789 if (m1 & xapp->AltMask)
1790 vm |= kfAlt;
1791 if (m1 & xapp->MetaMask)
1792 vm |= kfMeta;
1793 if (m1 & xapp->SuperMask)
1794 vm |= kfSuper;
1795 if (m1 & xapp->HyperMask)
1796 vm |= kfHyper;
1797 if (m1 & xapp->ModeSwitchMask)
1798 vm |= kfAltGr;
1799
1800 return vm;
1801 }
1802
getCharFromEvent(const XKeyEvent & key,char * s,int maxLen)1803 bool YWindow::getCharFromEvent(const XKeyEvent &key, char *s, int maxLen) {
1804 char keyBuf[16];
1805 KeySym ksym;
1806 XKeyEvent kev = key;
1807
1808 // FIXME:
1809 int klen = XLookupString(&kev, keyBuf, sizeof(keyBuf), &ksym, nullptr);
1810 #ifndef USE_XmbLookupString
1811 if ((klen == 0) && (ksym < 0x1000)) {
1812 klen = 1;
1813 keyBuf[0] = char(ksym & 0xFF);
1814 }
1815 #endif
1816 if (klen >= 1 && klen < maxLen - 1) {
1817 memcpy(s, keyBuf, size_t(klen));
1818 s[klen] = '\0';
1819 return true;
1820 }
1821 return false;
1822 }
1823
scrollWindow(int dx,int dy)1824 void YWindow::scrollWindow(int dx, int dy) {
1825 if (dx == 0 && dy == 0)
1826 return ;
1827
1828 if (dx >= int(width()) || dx <= -int(width()) ||
1829 dy >= int(height()) || dy <= -int(height()))
1830 {
1831 repaint();
1832 return ;
1833 }
1834
1835 Graphics &g = getGraphics();
1836 XRectangle r[2];
1837 int nr = 0;
1838
1839 XGCValues gcv;
1840 gcv.graphics_exposures = False;
1841 unsigned long gcvflags = GCGraphicsExposures;
1842 GC scrollGC = XCreateGC(xapp->display(), handle(), gcvflags, &gcv);
1843
1844 XCopyArea(xapp->display(), handle(), handle(), scrollGC,
1845 dx, dy, width(), height(), 0, 0);
1846
1847 XFreeGC(xapp->display(), scrollGC);
1848
1849 dx = - dx;
1850 dy = - dy;
1851
1852 if (dy != 0) {
1853 r[nr].x = 0;
1854 r[nr].width = static_cast<unsigned short>(width());
1855
1856 if (dy >= 0) {
1857 r[nr].y = 0;
1858 r[nr].height = static_cast<unsigned short>(dy);
1859 } else {
1860 r[nr].height = static_cast<unsigned short>(- dy);
1861 r[nr].y = short(int(height()) - int(r[nr].height));
1862 }
1863 nr++;
1864 }
1865 if (dx != 0) {
1866 r[nr].y = 0;
1867 r[nr].height = static_cast<unsigned short>(height()); // !!! optimize
1868
1869 if (dx >= 0) {
1870 r[nr].x = 0;
1871 r[nr].width = static_cast<unsigned short>(dx);
1872 } else {
1873 r[nr].width = static_cast<unsigned short>(- dx);
1874 r[nr].x = short(int(width()) - int(r[nr].width));
1875 }
1876 nr++;
1877 }
1878 //msg("nr=%d", nr);
1879
1880 g.setClipRectangles(r, nr);
1881
1882 XRectangle re;
1883 if (nr == 1)
1884 re = r[0];
1885 else {
1886 re.x = 0;
1887 re.y = 0;
1888 re.width = static_cast<unsigned short>(width());
1889 re.height = static_cast<unsigned short>(height());
1890 }
1891
1892 paint(g, YRect(re.x, re.y, re.width, re.height)); // !!! add flag to do minimal redraws
1893
1894 g.resetClip();
1895 }
1896
clearWindow()1897 void YWindow::clearWindow() {
1898 XClearWindow(xapp->display(), handle());
1899 }
1900
clearArea(int x,int y,unsigned w,unsigned h,bool exposures)1901 void YWindow::clearArea(int x, int y, unsigned w, unsigned h, bool exposures) {
1902 XClearArea(xapp->display(), handle(), x, y, w, h, exposures);
1903 }
1904
createPixmap()1905 Pixmap YWindow::createPixmap() {
1906 return XCreatePixmap(xapp->display(), xapp->root(), fWidth, fHeight, fDepth);
1907 }
1908
format()1909 XRenderPictFormat* YWindow::format() {
1910 return xapp->formatForDepth(depth());
1911 }
1912
createPicture()1913 Picture YWindow::createPicture() {
1914 Picture picture = None;
1915 XRenderPictFormat* format = this->format();
1916 if (format) {
1917 XRenderPictureAttributes attr;
1918 unsigned long mask = None;
1919 picture = XRenderCreatePicture(xapp->display(), handle(),
1920 format, mask, &attr);
1921 }
1922 return picture;
1923 }
1924
getScreen()1925 int YWindow::getScreen() {
1926 int dx = 0, dy = 0;
1927 mapToGlobal(dx, dy);
1928 return desktop->getScreenForRect(dx, dy, width(), height());
1929 }
1930
updateXineramaInfo(unsigned & horizontal,unsigned & vertical)1931 bool YDesktop::updateXineramaInfo(unsigned& horizontal, unsigned& vertical) {
1932 xiInfo.clear();
1933
1934 #ifdef CONFIG_XRANDR
1935 if (xrandr.supported && !xrrDisable) {
1936 XRRScreenResources *xrrsr =
1937 XRRGetScreenResources(xapp->display(), handle());
1938 for (int i = 0; xrrsr && i < xrrsr->ncrtc; i++) {
1939 XRRCrtcInfo *ci = XRRGetCrtcInfo(xapp->display(), xrrsr,
1940 xrrsr->crtcs[i]);
1941 MSG(("xrr %d (%lu): %d %d %u %u", i, xrrsr->crtcs[i],
1942 ci->x, ci->y, ci->width, ci->height));
1943 if (ci->width && ci->height) {
1944 DesktopScreenInfo si(int(xrrsr->crtcs[i]),
1945 ci->x, ci->y, ci->width, ci->height);
1946 xiInfo.append(si);
1947 }
1948 XRRFreeCrtcInfo(ci);
1949 }
1950
1951 if (xineramaPrimaryScreenName) {
1952 const char* name = xineramaPrimaryScreenName;
1953 MSG(("xinerama primary screen name: %s", name));
1954 for (int o = 0; xrrsr && o < xrrsr->noutput; o++) {
1955 XRROutputInfo *oinfo = XRRGetOutputInfo(xapp->display(), xrrsr,
1956 xrrsr->outputs[o]);
1957 MSG(("output: %s -> %lu", oinfo->name, oinfo->crtc));
1958 if (oinfo->name && strcmp(oinfo->name, name) == 0) {
1959 for (int k = 0; k < xiInfo.getCount(); k++) {
1960 if (xiInfo[k].screen_number == int(oinfo->crtc)) {
1961 xineramaPrimaryScreen = o;
1962 MSG(("xinerama primary screen: %s -> %d",
1963 oinfo->name, o));
1964 }
1965 }
1966 }
1967 XRRFreeOutputInfo(oinfo);
1968 }
1969 }
1970 XRRFreeScreenResources(xrrsr);
1971 }
1972 #endif
1973
1974 #ifdef XINERAMA
1975 if (xiInfo.getCount() < 2 && xinerama.supported) {
1976 // use xinerama if no XRANDR screens (nvidia hack)
1977 int count = 0;
1978 xsmart<XineramaScreenInfo> screens(
1979 XineramaQueryScreens(xapp->display(), &count));
1980 MSG(("xinerama: heads=%d", count));
1981 if (screens && count > xiInfo.getCount()) {
1982 xiInfo.clear();
1983 for (int i = 0; i < count; i++) {
1984 const XineramaScreenInfo& xine(screens[i]);
1985 MSG(("xinerama: %d +%d+%d %dx%d", xine.screen_number,
1986 xine.x_org, xine.y_org, xine.width, xine.height));
1987 DesktopScreenInfo si(i, xine.x_org, xine.y_org,
1988 unsigned(xine.width),
1989 unsigned(xine.height));
1990 xiInfo.append(si);
1991 }
1992 }
1993 }
1994 #endif
1995
1996 if (xiInfo.isEmpty()) {
1997 DesktopScreenInfo si(0, 0, 0,
1998 unsigned(xapp->displayWidth()),
1999 unsigned(xapp->displayHeight()));
2000 xiInfo.append(si);
2001 }
2002
2003 unsigned w = 0;
2004 unsigned h = 0;
2005 for (int i = 0; i < xiInfo.getCount(); i++) {
2006 const DesktopScreenInfo& info(xiInfo[i]);
2007 w = max(w, info.horizontal());
2008 h = max(h, info.vertical());
2009 MSG(("screen %d (%d): %d %d %u %u", i, info.screen_number,
2010 info.x_org, info.y_org, info.width, info.height));
2011 }
2012 swap(w, horizontal);
2013 swap(h, vertical);
2014 MSG(("desktop screen area: %u %u -> %u %u", w, h, horizontal, vertical));
2015 return w != horizontal || h != vertical;
2016 }
2017
getScreenInfo(int screen_no)2018 const DesktopScreenInfo& YDesktop::getScreenInfo(int screen_no) {
2019 int s = (screen_no == -1) ? xineramaPrimaryScreen : screen_no;
2020 if (s < 0 || s >= xiInfo.getCount())
2021 s = 0;
2022 if (xiInfo.isEmpty()) {
2023 xiInfo.append(DesktopScreenInfo(0, 0, 0,
2024 desktop->width(), desktop->height()));
2025 }
2026 return xiInfo[s];
2027 }
2028
getScreenGeometry(int screen_no)2029 YRect YDesktop::getScreenGeometry(int screen_no) {
2030 return getScreenInfo(screen_no);
2031 }
2032
getScreenGeometry(int * x,int * y,unsigned * width,unsigned * height,int screen_no)2033 void YDesktop::getScreenGeometry(int *x, int *y,
2034 unsigned *width, unsigned *height,
2035 int screen_no)
2036 {
2037 const DesktopScreenInfo& info(getScreenInfo(screen_no));
2038 *x = info.x_org;
2039 *y = info.y_org;
2040 *width = info.width;
2041 *height = info.height;
2042 }
2043
getScreenForRect(int x,int y,unsigned width,unsigned height)2044 int YDesktop::getScreenForRect(int x, int y, unsigned width, unsigned height) {
2045 int screen = 0;
2046 if (1 < xiInfo.getCount()) {
2047 long best = xiInfo[0].coverage(x, y, width, height);
2048 for (int s = 1; s < xiInfo.getCount(); s++) {
2049 long cov = xiInfo[s].coverage(x, y, width, height);
2050 if (cov > best) {
2051 best = cov;
2052 screen = s;
2053 }
2054 }
2055 }
2056 return screen;
2057 }
2058
keyCodeToKeySym(unsigned keycode,unsigned index)2059 KeySym YWindow::keyCodeToKeySym(unsigned keycode, unsigned index) {
2060 return XkbKeycodeToKeysym(xapp->display(), KeyCode(keycode), 0, index);
2061 }
2062
2063 // vim: set sw=4 ts=4 et:
2064