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