1/*
2 * Xephyr - A kdrive X server that runs in a host X window.
3 *          Authored by Matthew Allum <mallum@openedhand.com>
4 *
5 * Copyright © 2004 Nokia
6 *
7 * Permission to use, copy, modify, distribute, and sell this software and its
8 * documentation for any purpose is hereby granted without fee, provided that
9 * the above copyright notice appear in all copies and that both that
10 * copyright notice and this permission notice appear in supporting
11 * documentation, and that the name of Nokia not be used in
12 * advertising or publicity pertaining to distribution of the software without
13 * specific, written prior permission. Nokia makes no
14 * representations about the suitability of this software for any purpose.  It
15 * is provided "as is" without express or implied warranty.
16 *
17 * NOKIA DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE,
18 * INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO
19 * EVENT SHALL NOKIA BE LIABLE FOR ANY SPECIAL, INDIRECT OR
20 * CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE,
21 * DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER
22 * TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
23 * PERFORMANCE OF THIS SOFTWARE.
24 */
25
26#ifdef HAVE_DIX_CONFIG_H
27#include <dix-config.h>
28#endif
29
30#include <xcb/xcb_keysyms.h>
31#include <X11/keysym.h>
32
33#include "ephyr.h"
34
35#include "inputstr.h"
36#include "scrnintstr.h"
37#include "ephyrlog.h"
38
39#ifdef GLAMOR
40#include "glamor.h"
41#endif
42#include "ephyr_glamor_glx.h"
43#include "glx_extinit.h"
44#include "xkbsrv.h"
45
46extern Bool ephyr_glamor;
47
48KdKeyboardInfo *ephyrKbd;
49KdPointerInfo *ephyrMouse;
50Bool ephyrNoDRI = FALSE;
51Bool ephyrNoXV = FALSE;
52
53static int mouseState = 0;
54static Rotation ephyrRandr = RR_Rotate_0;
55
56typedef struct _EphyrInputPrivate {
57    Bool enabled;
58} EphyrKbdPrivate, EphyrPointerPrivate;
59
60Bool EphyrWantGrayScale = 0;
61Bool EphyrWantResize = 0;
62Bool EphyrWantNoHostGrab = 0;
63
64Bool
65ephyrInitialize(KdCardInfo * card, EphyrPriv * priv)
66{
67    OsSignal(SIGUSR1, hostx_handle_signal);
68
69    priv->base = 0;
70    priv->bytes_per_line = 0;
71    return TRUE;
72}
73
74Bool
75ephyrCardInit(KdCardInfo * card)
76{
77    EphyrPriv *priv;
78
79    priv = (EphyrPriv *) malloc(sizeof(EphyrPriv));
80    if (!priv)
81        return FALSE;
82
83    if (!ephyrInitialize(card, priv)) {
84        free(priv);
85        return FALSE;
86    }
87    card->driver = priv;
88
89    return TRUE;
90}
91
92Bool
93ephyrScreenInitialize(KdScreenInfo *screen)
94{
95    EphyrScrPriv *scrpriv = screen->driver;
96    int x = 0, y = 0;
97    int width = 640, height = 480;
98    CARD32 redMask, greenMask, blueMask;
99
100    if (hostx_want_screen_geometry(screen, &width, &height, &x, &y)
101        || !screen->width || !screen->height) {
102        screen->width = width;
103        screen->height = height;
104        screen->x = x;
105        screen->y = y;
106    }
107
108    if (EphyrWantGrayScale)
109        screen->fb.depth = 8;
110
111    if (screen->fb.depth && screen->fb.depth != hostx_get_depth()) {
112        if (screen->fb.depth < hostx_get_depth()
113            && (screen->fb.depth == 24 || screen->fb.depth == 16
114                || screen->fb.depth == 8)) {
115            scrpriv->server_depth = screen->fb.depth;
116        }
117        else
118            ErrorF
119                ("\nXephyr: requested screen depth not supported, setting to match hosts.\n");
120    }
121
122    screen->fb.depth = hostx_get_server_depth(screen);
123    screen->rate = 72;
124
125    if (screen->fb.depth <= 8) {
126        if (EphyrWantGrayScale)
127            screen->fb.visuals = ((1 << StaticGray) | (1 << GrayScale));
128        else
129            screen->fb.visuals = ((1 << StaticGray) |
130                                  (1 << GrayScale) |
131                                  (1 << StaticColor) |
132                                  (1 << PseudoColor) |
133                                  (1 << TrueColor) | (1 << DirectColor));
134
135        screen->fb.redMask = 0x00;
136        screen->fb.greenMask = 0x00;
137        screen->fb.blueMask = 0x00;
138        screen->fb.depth = 8;
139        screen->fb.bitsPerPixel = 8;
140    }
141    else {
142        screen->fb.visuals = (1 << TrueColor);
143
144        if (screen->fb.depth <= 15) {
145            screen->fb.depth = 15;
146            screen->fb.bitsPerPixel = 16;
147        }
148        else if (screen->fb.depth <= 16) {
149            screen->fb.depth = 16;
150            screen->fb.bitsPerPixel = 16;
151        }
152        else if (screen->fb.depth <= 24) {
153            screen->fb.depth = 24;
154            screen->fb.bitsPerPixel = 32;
155        }
156        else if (screen->fb.depth <= 30) {
157            screen->fb.depth = 30;
158            screen->fb.bitsPerPixel = 32;
159        }
160        else {
161            ErrorF("\nXephyr: Unsupported screen depth %d\n", screen->fb.depth);
162            return FALSE;
163        }
164
165        hostx_get_visual_masks(screen, &redMask, &greenMask, &blueMask);
166
167        screen->fb.redMask = (Pixel) redMask;
168        screen->fb.greenMask = (Pixel) greenMask;
169        screen->fb.blueMask = (Pixel) blueMask;
170
171    }
172
173    scrpriv->randr = screen->randr;
174
175    return ephyrMapFramebuffer(screen);
176}
177
178void *
179ephyrWindowLinear(ScreenPtr pScreen,
180                  CARD32 row,
181                  CARD32 offset, int mode, CARD32 *size, void *closure)
182{
183    KdScreenPriv(pScreen);
184    EphyrPriv *priv = pScreenPriv->card->driver;
185
186    if (!pScreenPriv->enabled)
187        return 0;
188
189    *size = priv->bytes_per_line;
190    return priv->base + row * priv->bytes_per_line + offset;
191}
192
193/**
194 * Figure out display buffer size. If fakexa is enabled, allocate a larger
195 * buffer so that fakexa has space to put offscreen pixmaps.
196 */
197int
198ephyrBufferHeight(KdScreenInfo * screen)
199{
200    int buffer_height;
201
202    if (ephyrFuncs.initAccel == NULL)
203        buffer_height = screen->height;
204    else
205        buffer_height = 3 * screen->height;
206    return buffer_height;
207}
208
209Bool
210ephyrMapFramebuffer(KdScreenInfo * screen)
211{
212    EphyrScrPriv *scrpriv = screen->driver;
213    EphyrPriv *priv = screen->card->driver;
214    KdPointerMatrix m;
215    int buffer_height;
216
217    EPHYR_LOG("screen->width: %d, screen->height: %d index=%d",
218              screen->width, screen->height, screen->mynum);
219
220    /*
221     * Use the rotation last applied to ourselves (in the Xephyr case the fb
222     * coordinate system moves independently of the pointer coordinate system).
223     */
224    KdComputePointerMatrix(&m, ephyrRandr, screen->width, screen->height);
225    KdSetPointerMatrix(&m);
226
227    buffer_height = ephyrBufferHeight(screen);
228
229    priv->base =
230        hostx_screen_init(screen, screen->x, screen->y,
231                          screen->width, screen->height, buffer_height,
232                          &priv->bytes_per_line, &screen->fb.bitsPerPixel);
233
234    if ((scrpriv->randr & RR_Rotate_0) && !(scrpriv->randr & RR_Reflect_All)) {
235        scrpriv->shadow = FALSE;
236
237        screen->fb.byteStride = priv->bytes_per_line;
238        screen->fb.pixelStride = screen->width;
239        screen->fb.frameBuffer = (CARD8 *) (priv->base);
240    }
241    else {
242        /* Rotated/Reflected so we need to use shadow fb */
243        scrpriv->shadow = TRUE;
244
245        EPHYR_LOG("allocing shadow");
246
247        KdShadowFbAlloc(screen,
248                        scrpriv->randr & (RR_Rotate_90 | RR_Rotate_270));
249    }
250
251    return TRUE;
252}
253
254void
255ephyrSetScreenSizes(ScreenPtr pScreen)
256{
257    KdScreenPriv(pScreen);
258    KdScreenInfo *screen = pScreenPriv->screen;
259    EphyrScrPriv *scrpriv = screen->driver;
260
261    if (scrpriv->randr & (RR_Rotate_0 | RR_Rotate_180)) {
262        pScreen->width = screen->width;
263        pScreen->height = screen->height;
264        pScreen->mmWidth = screen->width_mm;
265        pScreen->mmHeight = screen->height_mm;
266    }
267    else {
268        pScreen->width = screen->height;
269        pScreen->height = screen->width;
270        pScreen->mmWidth = screen->height_mm;
271        pScreen->mmHeight = screen->width_mm;
272    }
273}
274
275Bool
276ephyrUnmapFramebuffer(KdScreenInfo * screen)
277{
278    EphyrScrPriv *scrpriv = screen->driver;
279
280    if (scrpriv->shadow)
281        KdShadowFbFree(screen);
282
283    /* Note, priv->base will get freed when XImage recreated */
284
285    return TRUE;
286}
287
288void
289ephyrShadowUpdate(ScreenPtr pScreen, shadowBufPtr pBuf)
290{
291    KdScreenPriv(pScreen);
292    KdScreenInfo *screen = pScreenPriv->screen;
293
294    EPHYR_LOG("slow paint");
295
296    /* FIXME: Slow Rotated/Reflected updates could be much
297     * much faster efficiently updating via transforming
298     * pBuf->pDamage  regions
299     */
300    shadowUpdateRotatePacked(pScreen, pBuf);
301    hostx_paint_rect(screen, 0, 0, 0, 0, screen->width, screen->height);
302}
303
304static void
305ephyrInternalDamageRedisplay(ScreenPtr pScreen)
306{
307    KdScreenPriv(pScreen);
308    KdScreenInfo *screen = pScreenPriv->screen;
309    EphyrScrPriv *scrpriv = screen->driver;
310    RegionPtr pRegion;
311
312    if (!scrpriv || !scrpriv->pDamage)
313        return;
314
315    pRegion = DamageRegion(scrpriv->pDamage);
316
317    if (RegionNotEmpty(pRegion)) {
318        int nbox;
319        BoxPtr pbox;
320
321        if (ephyr_glamor) {
322            ephyr_glamor_damage_redisplay(scrpriv->glamor, pRegion);
323        } else {
324            nbox = RegionNumRects(pRegion);
325            pbox = RegionRects(pRegion);
326
327            while (nbox--) {
328                hostx_paint_rect(screen,
329                                 pbox->x1, pbox->y1,
330                                 pbox->x1, pbox->y1,
331                                 pbox->x2 - pbox->x1, pbox->y2 - pbox->y1);
332                pbox++;
333            }
334        }
335        DamageEmpty(scrpriv->pDamage);
336    }
337}
338
339static void
340ephyrXcbProcessEvents(Bool queued_only);
341
342static Bool
343ephyrEventWorkProc(ClientPtr client, void *closure)
344{
345    ephyrXcbProcessEvents(TRUE);
346    return TRUE;
347}
348
349static void
350ephyrScreenBlockHandler(ScreenPtr pScreen, void *timeout)
351{
352    KdScreenPriv(pScreen);
353    KdScreenInfo *screen = pScreenPriv->screen;
354    EphyrScrPriv *scrpriv = screen->driver;
355
356    pScreen->BlockHandler = scrpriv->BlockHandler;
357    (*pScreen->BlockHandler)(pScreen, timeout);
358    scrpriv->BlockHandler = pScreen->BlockHandler;
359    pScreen->BlockHandler = ephyrScreenBlockHandler;
360
361    if (scrpriv->pDamage)
362        ephyrInternalDamageRedisplay(pScreen);
363
364    if (hostx_has_queued_event()) {
365        if (!QueueWorkProc(ephyrEventWorkProc, NULL, NULL))
366            FatalError("cannot queue event processing in ephyr block handler");
367        AdjustWaitForDelay(timeout, 0);
368    }
369}
370
371Bool
372ephyrSetInternalDamage(ScreenPtr pScreen)
373{
374    KdScreenPriv(pScreen);
375    KdScreenInfo *screen = pScreenPriv->screen;
376    EphyrScrPriv *scrpriv = screen->driver;
377    PixmapPtr pPixmap = NULL;
378
379    scrpriv->pDamage = DamageCreate((DamageReportFunc) 0,
380                                    (DamageDestroyFunc) 0,
381                                    DamageReportNone, TRUE, pScreen, pScreen);
382
383    pPixmap = (*pScreen->GetScreenPixmap) (pScreen);
384
385    DamageRegister(&pPixmap->drawable, scrpriv->pDamage);
386
387    return TRUE;
388}
389
390void
391ephyrUnsetInternalDamage(ScreenPtr pScreen)
392{
393    KdScreenPriv(pScreen);
394    KdScreenInfo *screen = pScreenPriv->screen;
395    EphyrScrPriv *scrpriv = screen->driver;
396
397    DamageDestroy(scrpriv->pDamage);
398    scrpriv->pDamage = NULL;
399}
400
401#ifdef RANDR
402Bool
403ephyrRandRGetInfo(ScreenPtr pScreen, Rotation * rotations)
404{
405    KdScreenPriv(pScreen);
406    KdScreenInfo *screen = pScreenPriv->screen;
407    EphyrScrPriv *scrpriv = screen->driver;
408    RRScreenSizePtr pSize;
409    Rotation randr;
410    int n = 0;
411
412    struct {
413        int width, height;
414    } sizes[] = {
415        {1600, 1200},
416        {1400, 1050},
417        {1280, 960},
418        {1280, 1024},
419        {1152, 864},
420        {1024, 768},
421        {832, 624},
422        {800, 600},
423        {720, 400},
424        {480, 640},
425        {640, 480},
426        {640, 400},
427        {320, 240},
428        {240, 320},
429        {160, 160},
430        {0, 0}
431    };
432
433    EPHYR_LOG("mark");
434
435    *rotations = RR_Rotate_All | RR_Reflect_All;
436
437    if (!hostx_want_preexisting_window(screen)
438        && !hostx_want_fullscreen()) {  /* only if no -parent switch */
439        while (sizes[n].width != 0 && sizes[n].height != 0) {
440            RRRegisterSize(pScreen,
441                           sizes[n].width,
442                           sizes[n].height,
443                           (sizes[n].width * screen->width_mm) / screen->width,
444                           (sizes[n].height * screen->height_mm) /
445                           screen->height);
446            n++;
447        }
448    }
449
450    pSize = RRRegisterSize(pScreen,
451                           screen->width,
452                           screen->height, screen->width_mm, screen->height_mm);
453
454    randr = KdSubRotation(scrpriv->randr, screen->randr);
455
456    RRSetCurrentConfig(pScreen, randr, 0, pSize);
457
458    return TRUE;
459}
460
461Bool
462ephyrRandRSetConfig(ScreenPtr pScreen,
463                    Rotation randr, int rate, RRScreenSizePtr pSize)
464{
465    KdScreenPriv(pScreen);
466    KdScreenInfo *screen = pScreenPriv->screen;
467    EphyrScrPriv *scrpriv = screen->driver;
468    Bool wasEnabled = pScreenPriv->enabled;
469    EphyrScrPriv oldscr;
470    int oldwidth, oldheight, oldmmwidth, oldmmheight;
471    Bool oldshadow;
472    int newwidth, newheight;
473
474    if (screen->randr & (RR_Rotate_0 | RR_Rotate_180)) {
475        newwidth = pSize->width;
476        newheight = pSize->height;
477    }
478    else {
479        newwidth = pSize->height;
480        newheight = pSize->width;
481    }
482
483    if (wasEnabled)
484        KdDisableScreen(pScreen);
485
486    oldscr = *scrpriv;
487
488    oldwidth = screen->width;
489    oldheight = screen->height;
490    oldmmwidth = pScreen->mmWidth;
491    oldmmheight = pScreen->mmHeight;
492    oldshadow = scrpriv->shadow;
493
494    /*
495     * Set new configuration
496     */
497
498    /*
499     * We need to store the rotation value for pointer coords transformation;
500     * though initially the pointer and fb rotation are identical, when we map
501     * the fb, the screen will be reinitialized and return into an unrotated
502     * state (presumably the HW is taking care of the rotation of the fb), but the
503     * pointer still needs to be transformed.
504     */
505    ephyrRandr = KdAddRotation(screen->randr, randr);
506    scrpriv->randr = ephyrRandr;
507
508    ephyrUnmapFramebuffer(screen);
509
510    screen->width = newwidth;
511    screen->height = newheight;
512
513    scrpriv->win_width = screen->width;
514    scrpriv->win_height = screen->height;
515#ifdef GLAMOR
516    ephyr_glamor_set_window_size(scrpriv->glamor,
517                                 scrpriv->win_width,
518                                 scrpriv->win_height);
519#endif
520
521    if (!ephyrMapFramebuffer(screen))
522        goto bail4;
523
524    /* FIXME below should go in own call */
525
526    if (oldshadow)
527        KdShadowUnset(screen->pScreen);
528    else
529        ephyrUnsetInternalDamage(screen->pScreen);
530
531    ephyrSetScreenSizes(screen->pScreen);
532
533    if (scrpriv->shadow) {
534        if (!KdShadowSet(screen->pScreen,
535                         scrpriv->randr, ephyrShadowUpdate, ephyrWindowLinear))
536            goto bail4;
537    }
538    else {
539#ifdef GLAMOR
540        if (ephyr_glamor)
541            ephyr_glamor_create_screen_resources(pScreen);
542#endif
543        /* Without shadow fb ( non rotated ) we need
544         * to use damage to efficiently update display
545         * via signal regions what to copy from 'fb'.
546         */
547        if (!ephyrSetInternalDamage(screen->pScreen))
548            goto bail4;
549    }
550
551    /*
552     * Set frame buffer mapping
553     */
554    (*pScreen->ModifyPixmapHeader) (fbGetScreenPixmap(pScreen),
555                                    pScreen->width,
556                                    pScreen->height,
557                                    screen->fb.depth,
558                                    screen->fb.bitsPerPixel,
559                                    screen->fb.byteStride,
560                                    screen->fb.frameBuffer);
561
562    /* set the subpixel order */
563
564    KdSetSubpixelOrder(pScreen, scrpriv->randr);
565
566    if (wasEnabled)
567        KdEnableScreen(pScreen);
568
569    RRScreenSizeNotify(pScreen);
570
571    return TRUE;
572
573 bail4:
574    EPHYR_LOG("bailed");
575
576    ephyrUnmapFramebuffer(screen);
577    *scrpriv = oldscr;
578    (void) ephyrMapFramebuffer(screen);
579
580    pScreen->width = oldwidth;
581    pScreen->height = oldheight;
582    pScreen->mmWidth = oldmmwidth;
583    pScreen->mmHeight = oldmmheight;
584
585    if (wasEnabled)
586        KdEnableScreen(pScreen);
587    return FALSE;
588}
589
590Bool
591ephyrRandRInit(ScreenPtr pScreen)
592{
593    rrScrPrivPtr pScrPriv;
594
595    if (!RRScreenInit(pScreen))
596        return FALSE;
597
598    pScrPriv = rrGetScrPriv(pScreen);
599    pScrPriv->rrGetInfo = ephyrRandRGetInfo;
600    pScrPriv->rrSetConfig = ephyrRandRSetConfig;
601    return TRUE;
602}
603
604static Bool
605ephyrResizeScreen (ScreenPtr           pScreen,
606                  int                  newwidth,
607                  int                  newheight)
608{
609    KdScreenPriv(pScreen);
610    KdScreenInfo *screen = pScreenPriv->screen;
611    RRScreenSize size = {0};
612    Bool ret;
613    int t;
614
615    if (screen->randr & (RR_Rotate_90|RR_Rotate_270)) {
616        t = newwidth;
617        newwidth = newheight;
618        newheight = t;
619    }
620
621    if (newwidth == screen->width && newheight == screen->height) {
622        return FALSE;
623    }
624
625    size.width = newwidth;
626    size.height = newheight;
627
628    hostx_size_set_from_configure(TRUE);
629    ret = ephyrRandRSetConfig (pScreen, screen->randr, 0, &size);
630    hostx_size_set_from_configure(FALSE);
631    if (ret) {
632        RROutputPtr output;
633
634        output = RRFirstOutput(pScreen);
635        if (!output)
636            return FALSE;
637        RROutputSetModes(output, NULL, 0, 0);
638    }
639
640    return ret;
641}
642#endif
643
644Bool
645ephyrCreateColormap(ColormapPtr pmap)
646{
647    return fbInitializeColormap(pmap);
648}
649
650Bool
651ephyrInitScreen(ScreenPtr pScreen)
652{
653    KdScreenPriv(pScreen);
654    KdScreenInfo *screen = pScreenPriv->screen;
655
656    EPHYR_LOG("pScreen->myNum:%d\n", pScreen->myNum);
657    hostx_set_screen_number(screen, pScreen->myNum);
658    if (EphyrWantNoHostGrab) {
659        hostx_set_win_title(screen, "xephyr");
660    } else {
661        hostx_set_win_title(screen, "(ctrl+shift grabs mouse and keyboard)");
662    }
663    pScreen->CreateColormap = ephyrCreateColormap;
664
665#ifdef XV
666    if (!ephyrNoXV) {
667        if (ephyr_glamor)
668            ephyr_glamor_xv_init(pScreen);
669        else if (!ephyrInitVideo(pScreen)) {
670            EPHYR_LOG_ERROR("failed to initialize xvideo\n");
671        }
672        else {
673            EPHYR_LOG("initialized xvideo okay\n");
674        }
675    }
676#endif /*XV*/
677
678    return TRUE;
679}
680
681
682Bool
683ephyrFinishInitScreen(ScreenPtr pScreen)
684{
685    KdScreenPriv(pScreen);
686    KdScreenInfo *screen = pScreenPriv->screen;
687    EphyrScrPriv *scrpriv = screen->driver;
688
689    /* FIXME: Calling this even if not using shadow.
690     * Seems harmless enough. But may be safer elsewhere.
691     */
692    if (!shadowSetup(pScreen))
693        return FALSE;
694
695#ifdef RANDR
696    if (!ephyrRandRInit(pScreen))
697        return FALSE;
698#endif
699
700    scrpriv->BlockHandler = pScreen->BlockHandler;
701    pScreen->BlockHandler = ephyrScreenBlockHandler;
702
703    return TRUE;
704}
705
706/**
707 * Called by kdrive after calling down the
708 * pScreen->CreateScreenResources() chain, this gives us a chance to
709 * make any pixmaps after the screen and all extensions have been
710 * initialized.
711 */
712Bool
713ephyrCreateResources(ScreenPtr pScreen)
714{
715    KdScreenPriv(pScreen);
716    KdScreenInfo *screen = pScreenPriv->screen;
717    EphyrScrPriv *scrpriv = screen->driver;
718
719    EPHYR_LOG("mark pScreen=%p mynum=%d shadow=%d",
720              pScreen, pScreen->myNum, scrpriv->shadow);
721
722    if (scrpriv->shadow)
723        return KdShadowSet(pScreen,
724                           scrpriv->randr,
725                           ephyrShadowUpdate, ephyrWindowLinear);
726    else {
727#ifdef GLAMOR
728        if (ephyr_glamor) {
729            if (!ephyr_glamor_create_screen_resources(pScreen))
730                return FALSE;
731        }
732#endif
733        return ephyrSetInternalDamage(pScreen);
734    }
735}
736
737void
738ephyrScreenFini(KdScreenInfo * screen)
739{
740    EphyrScrPriv *scrpriv = screen->driver;
741
742    if (scrpriv->shadow) {
743        KdShadowFbFree(screen);
744    }
745    scrpriv->BlockHandler = NULL;
746}
747
748void
749ephyrCloseScreen(ScreenPtr pScreen)
750{
751    ephyrUnsetInternalDamage(pScreen);
752}
753
754/*
755 * Port of Mark McLoughlin's Xnest fix for focus in + modifier bug.
756 * See https://bugs.freedesktop.org/show_bug.cgi?id=3030
757 */
758void
759ephyrUpdateModifierState(unsigned int state)
760{
761
762    DeviceIntPtr pDev = inputInfo.keyboard;
763    KeyClassPtr keyc = pDev->key;
764    int i;
765    CARD8 mask;
766    int xkb_state;
767
768    if (!pDev)
769        return;
770
771    xkb_state = XkbStateFieldFromRec(&pDev->key->xkbInfo->state);
772    state = state & 0xff;
773
774    if (xkb_state == state)
775        return;
776
777    for (i = 0, mask = 1; i < 8; i++, mask <<= 1) {
778        int key;
779
780        /* Modifier is down, but shouldn't be */
781        if ((xkb_state & mask) && !(state & mask)) {
782            int count = keyc->modifierKeyCount[i];
783
784            for (key = 0; key < MAP_LENGTH; key++)
785                if (keyc->xkbInfo->desc->map->modmap[key] & mask) {
786                    if (mask == XCB_MOD_MASK_LOCK) {
787                        KdEnqueueKeyboardEvent(ephyrKbd, key, FALSE);
788                        KdEnqueueKeyboardEvent(ephyrKbd, key, TRUE);
789                    }
790                    else if (key_is_down(pDev, key, KEY_PROCESSED))
791                        KdEnqueueKeyboardEvent(ephyrKbd, key, TRUE);
792
793                    if (--count == 0)
794                        break;
795                }
796        }
797
798        /* Modifier should be down, but isn't */
799        if (!(xkb_state & mask) && (state & mask))
800            for (key = 0; key < MAP_LENGTH; key++)
801                if (keyc->xkbInfo->desc->map->modmap[key] & mask) {
802                    KdEnqueueKeyboardEvent(ephyrKbd, key, FALSE);
803                    if (mask == XCB_MOD_MASK_LOCK)
804                        KdEnqueueKeyboardEvent(ephyrKbd, key, TRUE);
805                    break;
806                }
807    }
808}
809
810static Bool
811ephyrCursorOffScreen(ScreenPtr *ppScreen, int *x, int *y)
812{
813    return FALSE;
814}
815
816static void
817ephyrCrossScreen(ScreenPtr pScreen, Bool entering)
818{
819}
820
821ScreenPtr ephyrCursorScreen; /* screen containing the cursor */
822
823static void
824ephyrWarpCursor(DeviceIntPtr pDev, ScreenPtr pScreen, int x, int y)
825{
826    input_lock();
827    ephyrCursorScreen = pScreen;
828    miPointerWarpCursor(inputInfo.pointer, pScreen, x, y);
829
830    input_unlock();
831}
832
833miPointerScreenFuncRec ephyrPointerScreenFuncs = {
834    ephyrCursorOffScreen,
835    ephyrCrossScreen,
836    ephyrWarpCursor,
837};
838
839static KdScreenInfo *
840screen_from_window(Window w)
841{
842    int i = 0;
843
844    for (i = 0; i < screenInfo.numScreens; i++) {
845        ScreenPtr pScreen = screenInfo.screens[i];
846        KdPrivScreenPtr kdscrpriv = KdGetScreenPriv(pScreen);
847        KdScreenInfo *screen = kdscrpriv->screen;
848        EphyrScrPriv *scrpriv = screen->driver;
849
850        if (scrpriv->win == w
851            || scrpriv->peer_win == w
852            || scrpriv->win_pre_existing == w) {
853            return screen;
854        }
855    }
856
857    return NULL;
858}
859
860static void
861ephyrProcessErrorEvent(xcb_generic_event_t *xev)
862{
863    xcb_generic_error_t *e = (xcb_generic_error_t *)xev;
864
865    FatalError("X11 error\n"
866               "Error code: %hhu\n"
867               "Sequence number: %hu\n"
868               "Major code: %hhu\tMinor code: %hu\n"
869               "Error value: %u\n",
870               e->error_code,
871               e->sequence,
872               e->major_code, e->minor_code,
873               e->resource_id);
874}
875
876static void
877ephyrProcessExpose(xcb_generic_event_t *xev)
878{
879    xcb_expose_event_t *expose = (xcb_expose_event_t *)xev;
880    KdScreenInfo *screen = screen_from_window(expose->window);
881    EphyrScrPriv *scrpriv = screen->driver;
882
883    /* Wait for the last expose event in a series of cliprects
884     * to actually paint our screen.
885     */
886    if (expose->count != 0)
887        return;
888
889    if (scrpriv) {
890        hostx_paint_rect(scrpriv->screen, 0, 0, 0, 0,
891                         scrpriv->win_width,
892                         scrpriv->win_height);
893    } else {
894        EPHYR_LOG_ERROR("failed to get host screen\n");
895    }
896}
897
898static void
899ephyrProcessMouseMotion(xcb_generic_event_t *xev)
900{
901    xcb_motion_notify_event_t *motion = (xcb_motion_notify_event_t *)xev;
902    KdScreenInfo *screen = screen_from_window(motion->event);
903
904    if (!ephyrMouse ||
905        !((EphyrPointerPrivate *) ephyrMouse->driverPrivate)->enabled) {
906        EPHYR_LOG("skipping mouse motion:%d\n", screen->pScreen->myNum);
907        return;
908    }
909
910    if (ephyrCursorScreen != screen->pScreen) {
911        EPHYR_LOG("warping mouse cursor. "
912                  "cur_screen:%d, motion_screen:%d\n",
913                  ephyrCursorScreen->myNum, screen->pScreen->myNum);
914        ephyrWarpCursor(inputInfo.pointer, screen->pScreen,
915                        motion->event_x, motion->event_y);
916    }
917    else {
918        int x = 0, y = 0;
919
920        EPHYR_LOG("enqueuing mouse motion:%d\n", screen->pScreen->myNum);
921        x = motion->event_x;
922        y = motion->event_y;
923        EPHYR_LOG("initial (x,y):(%d,%d)\n", x, y);
924
925        /* convert coords into desktop-wide coordinates.
926         * fill_pointer_events will convert that back to
927         * per-screen coordinates where needed */
928        x += screen->pScreen->x;
929        y += screen->pScreen->y;
930
931        KdEnqueuePointerEvent(ephyrMouse, mouseState | KD_POINTER_DESKTOP, x, y, 0);
932    }
933}
934
935static void
936ephyrProcessButtonPress(xcb_generic_event_t *xev)
937{
938    xcb_button_press_event_t *button = (xcb_button_press_event_t *)xev;
939
940    if (!ephyrMouse ||
941        !((EphyrPointerPrivate *) ephyrMouse->driverPrivate)->enabled) {
942        EPHYR_LOG("skipping mouse press:%d\n", screen_from_window(button->event)->pScreen->myNum);
943        return;
944    }
945
946    ephyrUpdateModifierState(button->state);
947    /* This is a bit hacky. will break for button 5 ( defined as 0x10 )
948     * Check KD_BUTTON defines in kdrive.h
949     */
950    mouseState |= 1 << (button->detail - 1);
951
952    EPHYR_LOG("enqueuing mouse press:%d\n", screen_from_window(button->event)->pScreen->myNum);
953    KdEnqueuePointerEvent(ephyrMouse, mouseState | KD_MOUSE_DELTA, 0, 0, 0);
954}
955
956static void
957ephyrProcessButtonRelease(xcb_generic_event_t *xev)
958{
959    xcb_button_press_event_t *button = (xcb_button_press_event_t *)xev;
960
961    if (!ephyrMouse ||
962        !((EphyrPointerPrivate *) ephyrMouse->driverPrivate)->enabled) {
963        return;
964    }
965
966    ephyrUpdateModifierState(button->state);
967    mouseState &= ~(1 << (button->detail - 1));
968
969    EPHYR_LOG("enqueuing mouse release:%d\n", screen_from_window(button->event)->pScreen->myNum);
970    KdEnqueuePointerEvent(ephyrMouse, mouseState | KD_MOUSE_DELTA, 0, 0, 0);
971}
972
973/* Xephyr wants ctrl+shift to grab the window, but that conflicts with
974   ctrl+alt+shift key combos. Remember the modifier state on key presses and
975   releases, if mod1 is pressed, we need ctrl, shift and mod1 released
976   before we allow a shift-ctrl grab activation.
977
978   note: a key event contains the mask _before_ the current key takes
979   effect, so mod1_was_down will be reset on the first key press after all
980   three were released, not on the last release. That'd require some more
981   effort.
982 */
983static int
984ephyrUpdateGrabModifierState(int state)
985{
986    static int mod1_was_down = 0;
987
988    if ((state & (XCB_MOD_MASK_CONTROL|XCB_MOD_MASK_SHIFT|XCB_MOD_MASK_1)) == 0)
989        mod1_was_down = 0;
990    else if (state & XCB_MOD_MASK_1)
991        mod1_was_down = 1;
992
993    return mod1_was_down;
994}
995
996static void
997ephyrProcessKeyPress(xcb_generic_event_t *xev)
998{
999    xcb_key_press_event_t *key = (xcb_key_press_event_t *)xev;
1000
1001    if (!ephyrKbd ||
1002        !((EphyrKbdPrivate *) ephyrKbd->driverPrivate)->enabled) {
1003        return;
1004    }
1005
1006    ephyrUpdateGrabModifierState(key->state);
1007    ephyrUpdateModifierState(key->state);
1008    KdEnqueueKeyboardEvent(ephyrKbd, key->detail, FALSE);
1009}
1010
1011static void
1012ephyrProcessKeyRelease(xcb_generic_event_t *xev)
1013{
1014    xcb_connection_t *conn = hostx_get_xcbconn();
1015    xcb_key_release_event_t *key = (xcb_key_release_event_t *)xev;
1016    static xcb_key_symbols_t *keysyms;
1017    static int grabbed_screen = -1;
1018    int mod1_down = ephyrUpdateGrabModifierState(key->state);
1019
1020    if (!keysyms)
1021        keysyms = xcb_key_symbols_alloc(conn);
1022
1023    if (!EphyrWantNoHostGrab &&
1024        (((xcb_key_symbols_get_keysym(keysyms, key->detail, 0) == XK_Shift_L
1025          || xcb_key_symbols_get_keysym(keysyms, key->detail, 0) == XK_Shift_R)
1026         && (key->state & XCB_MOD_MASK_CONTROL)) ||
1027        ((xcb_key_symbols_get_keysym(keysyms, key->detail, 0) == XK_Control_L
1028          || xcb_key_symbols_get_keysym(keysyms, key->detail, 0) == XK_Control_R)
1029         && (key->state & XCB_MOD_MASK_SHIFT)))) {
1030        KdScreenInfo *screen = screen_from_window(key->event);
1031        EphyrScrPriv *scrpriv = screen->driver;
1032
1033        if (grabbed_screen != -1) {
1034            xcb_ungrab_keyboard(conn, XCB_TIME_CURRENT_TIME);
1035            xcb_ungrab_pointer(conn, XCB_TIME_CURRENT_TIME);
1036            grabbed_screen = -1;
1037            hostx_set_win_title(screen,
1038                                "(ctrl+shift grabs mouse and keyboard)");
1039        }
1040        else if (!mod1_down) {
1041            /* Attempt grab */
1042            xcb_grab_keyboard_cookie_t kbgrabc =
1043                xcb_grab_keyboard(conn,
1044                                  TRUE,
1045                                  scrpriv->win,
1046                                  XCB_TIME_CURRENT_TIME,
1047                                  XCB_GRAB_MODE_ASYNC,
1048                                  XCB_GRAB_MODE_ASYNC);
1049            xcb_grab_keyboard_reply_t *kbgrabr;
1050            xcb_grab_pointer_cookie_t pgrabc =
1051                xcb_grab_pointer(conn,
1052                                 TRUE,
1053                                 scrpriv->win,
1054                                 0,
1055                                 XCB_GRAB_MODE_ASYNC,
1056                                 XCB_GRAB_MODE_ASYNC,
1057                                 scrpriv->win,
1058                                 XCB_NONE,
1059                                 XCB_TIME_CURRENT_TIME);
1060            xcb_grab_pointer_reply_t *pgrabr;
1061            kbgrabr = xcb_grab_keyboard_reply(conn, kbgrabc, NULL);
1062            if (!kbgrabr || kbgrabr->status != XCB_GRAB_STATUS_SUCCESS) {
1063                xcb_discard_reply(conn, pgrabc.sequence);
1064                xcb_ungrab_pointer(conn, XCB_TIME_CURRENT_TIME);
1065            } else {
1066                pgrabr = xcb_grab_pointer_reply(conn, pgrabc, NULL);
1067                if (!pgrabr || pgrabr->status != XCB_GRAB_STATUS_SUCCESS)
1068                    {
1069                        xcb_ungrab_keyboard(conn,
1070                                            XCB_TIME_CURRENT_TIME);
1071                    } else {
1072                    grabbed_screen = scrpriv->mynum;
1073                    hostx_set_win_title
1074                        (screen,
1075                         "(ctrl+shift releases mouse and keyboard)");
1076                }
1077            }
1078        }
1079    }
1080
1081    if (!ephyrKbd ||
1082        !((EphyrKbdPrivate *) ephyrKbd->driverPrivate)->enabled) {
1083        return;
1084    }
1085
1086    /* Still send the release event even if above has happened server
1087     * will get confused with just an up event.  Maybe it would be
1088     * better to just block shift+ctrls getting to kdrive all
1089     * together.
1090     */
1091    ephyrUpdateModifierState(key->state);
1092    KdEnqueueKeyboardEvent(ephyrKbd, key->detail, TRUE);
1093}
1094
1095static void
1096ephyrProcessConfigureNotify(xcb_generic_event_t *xev)
1097{
1098    xcb_configure_notify_event_t *configure =
1099        (xcb_configure_notify_event_t *)xev;
1100    KdScreenInfo *screen = screen_from_window(configure->window);
1101    EphyrScrPriv *scrpriv = screen->driver;
1102
1103    if (!scrpriv ||
1104        (scrpriv->win_pre_existing == None && !EphyrWantResize)) {
1105        return;
1106    }
1107
1108#ifdef RANDR
1109    ephyrResizeScreen(screen->pScreen, configure->width, configure->height);
1110#endif /* RANDR */
1111}
1112
1113static void
1114ephyrXcbProcessEvents(Bool queued_only)
1115{
1116    xcb_connection_t *conn = hostx_get_xcbconn();
1117    xcb_generic_event_t *expose = NULL, *configure = NULL;
1118
1119    while (TRUE) {
1120        xcb_generic_event_t *xev = hostx_get_event(queued_only);
1121
1122        if (!xev) {
1123            /* If our XCB connection has died (for example, our window was
1124             * closed), exit now.
1125             */
1126            if (xcb_connection_has_error(conn)) {
1127                CloseWellKnownConnections();
1128                OsCleanup(1);
1129                exit(1);
1130            }
1131
1132            break;
1133        }
1134
1135        switch (xev->response_type & 0x7f) {
1136        case 0:
1137            ephyrProcessErrorEvent(xev);
1138            break;
1139
1140        case XCB_EXPOSE:
1141            free(expose);
1142            expose = xev;
1143            xev = NULL;
1144            break;
1145
1146        case XCB_MOTION_NOTIFY:
1147            ephyrProcessMouseMotion(xev);
1148            break;
1149
1150        case XCB_KEY_PRESS:
1151            ephyrProcessKeyPress(xev);
1152            break;
1153
1154        case XCB_KEY_RELEASE:
1155            ephyrProcessKeyRelease(xev);
1156            break;
1157
1158        case XCB_BUTTON_PRESS:
1159            ephyrProcessButtonPress(xev);
1160            break;
1161
1162        case XCB_BUTTON_RELEASE:
1163            ephyrProcessButtonRelease(xev);
1164            break;
1165
1166        case XCB_CONFIGURE_NOTIFY:
1167            free(configure);
1168            configure = xev;
1169            xev = NULL;
1170            break;
1171        }
1172
1173        if (xev) {
1174            if (ephyr_glamor)
1175                ephyr_glamor_process_event(xev);
1176
1177            free(xev);
1178        }
1179    }
1180
1181    if (configure) {
1182        ephyrProcessConfigureNotify(configure);
1183        free(configure);
1184    }
1185
1186    if (expose) {
1187        ephyrProcessExpose(expose);
1188        free(expose);
1189    }
1190}
1191
1192static void
1193ephyrXcbNotify(int fd, int ready, void *data)
1194{
1195    ephyrXcbProcessEvents(FALSE);
1196}
1197
1198void
1199ephyrCardFini(KdCardInfo * card)
1200{
1201    EphyrPriv *priv = card->driver;
1202
1203    free(priv);
1204}
1205
1206void
1207ephyrGetColors(ScreenPtr pScreen, int n, xColorItem * pdefs)
1208{
1209    /* XXX Not sure if this is right */
1210
1211    EPHYR_LOG("mark");
1212
1213    while (n--) {
1214        pdefs->red = 0;
1215        pdefs->green = 0;
1216        pdefs->blue = 0;
1217        pdefs++;
1218    }
1219
1220}
1221
1222void
1223ephyrPutColors(ScreenPtr pScreen, int n, xColorItem * pdefs)
1224{
1225    KdScreenPriv(pScreen);
1226    KdScreenInfo *screen = pScreenPriv->screen;
1227    EphyrScrPriv *scrpriv = screen->driver;
1228    int min, max, p;
1229
1230    /* XXX Not sure if this is right */
1231
1232    min = 256;
1233    max = 0;
1234
1235    while (n--) {
1236        p = pdefs->pixel;
1237        if (p < min)
1238            min = p;
1239        if (p > max)
1240            max = p;
1241
1242        hostx_set_cmap_entry(pScreen, p,
1243                             pdefs->red >> 8,
1244                             pdefs->green >> 8, pdefs->blue >> 8);
1245        pdefs++;
1246    }
1247    if (scrpriv->pDamage) {
1248        BoxRec box;
1249        RegionRec region;
1250
1251        box.x1 = 0;
1252        box.y1 = 0;
1253        box.x2 = pScreen->width;
1254        box.y2 = pScreen->height;
1255        RegionInit(&region, &box, 1);
1256        DamageReportDamage(scrpriv->pDamage, &region);
1257        RegionUninit(&region);
1258    }
1259}
1260
1261/* Mouse calls */
1262
1263static Status
1264MouseInit(KdPointerInfo * pi)
1265{
1266    pi->driverPrivate = (EphyrPointerPrivate *)
1267        calloc(sizeof(EphyrPointerPrivate), 1);
1268    ((EphyrPointerPrivate *) pi->driverPrivate)->enabled = FALSE;
1269    pi->nAxes = 3;
1270    pi->nButtons = 32;
1271    free(pi->name);
1272    pi->name = strdup("Xephyr virtual mouse");
1273
1274    /*
1275     * Must transform pointer coords since the pointer position
1276     * relative to the Xephyr window is controlled by the host server and
1277     * remains constant regardless of any rotation applied to the Xephyr screen.
1278     */
1279    pi->transformCoordinates = TRUE;
1280
1281    ephyrMouse = pi;
1282    return Success;
1283}
1284
1285static Status
1286MouseEnable(KdPointerInfo * pi)
1287{
1288    ((EphyrPointerPrivate *) pi->driverPrivate)->enabled = TRUE;
1289    SetNotifyFd(hostx_get_fd(), ephyrXcbNotify, X_NOTIFY_READ, NULL);
1290    return Success;
1291}
1292
1293static void
1294MouseDisable(KdPointerInfo * pi)
1295{
1296    ((EphyrPointerPrivate *) pi->driverPrivate)->enabled = FALSE;
1297    RemoveNotifyFd(hostx_get_fd());
1298    return;
1299}
1300
1301static void
1302MouseFini(KdPointerInfo * pi)
1303{
1304    free(pi->driverPrivate);
1305    ephyrMouse = NULL;
1306    return;
1307}
1308
1309KdPointerDriver EphyrMouseDriver = {
1310    "ephyr",
1311    MouseInit,
1312    MouseEnable,
1313    MouseDisable,
1314    MouseFini,
1315    NULL,
1316};
1317
1318/* Keyboard */
1319
1320static Status
1321EphyrKeyboardInit(KdKeyboardInfo * ki)
1322{
1323    KeySymsRec keySyms;
1324    CARD8 modmap[MAP_LENGTH];
1325    XkbControlsRec controls;
1326
1327    ki->driverPrivate = (EphyrKbdPrivate *)
1328        calloc(sizeof(EphyrKbdPrivate), 1);
1329
1330    if (hostx_load_keymap(&keySyms, modmap, &controls)) {
1331        XkbApplyMappingChange(ki->dixdev, &keySyms,
1332                              keySyms.minKeyCode,
1333                              keySyms.maxKeyCode - keySyms.minKeyCode + 1,
1334                              modmap, serverClient);
1335        XkbDDXChangeControls(ki->dixdev, &controls, &controls);
1336        free(keySyms.map);
1337    }
1338
1339    ki->minScanCode = keySyms.minKeyCode;
1340    ki->maxScanCode = keySyms.maxKeyCode;
1341
1342    if (ki->name != NULL) {
1343        free(ki->name);
1344    }
1345
1346    ki->name = strdup("Xephyr virtual keyboard");
1347    ephyrKbd = ki;
1348    return Success;
1349}
1350
1351static Status
1352EphyrKeyboardEnable(KdKeyboardInfo * ki)
1353{
1354    ((EphyrKbdPrivate *) ki->driverPrivate)->enabled = TRUE;
1355
1356    return Success;
1357}
1358
1359static void
1360EphyrKeyboardDisable(KdKeyboardInfo * ki)
1361{
1362    ((EphyrKbdPrivate *) ki->driverPrivate)->enabled = FALSE;
1363}
1364
1365static void
1366EphyrKeyboardFini(KdKeyboardInfo * ki)
1367{
1368    free(ki->driverPrivate);
1369    ephyrKbd = NULL;
1370    return;
1371}
1372
1373static void
1374EphyrKeyboardLeds(KdKeyboardInfo * ki, int leds)
1375{
1376}
1377
1378static void
1379EphyrKeyboardBell(KdKeyboardInfo * ki, int volume, int frequency, int duration)
1380{
1381}
1382
1383KdKeyboardDriver EphyrKeyboardDriver = {
1384    "ephyr",
1385    EphyrKeyboardInit,
1386    EphyrKeyboardEnable,
1387    EphyrKeyboardLeds,
1388    EphyrKeyboardBell,
1389    EphyrKeyboardDisable,
1390    EphyrKeyboardFini,
1391    NULL,
1392};
1393