1/*
2 * Xplugin rootless implementation frame functions
3 *
4 * Copyright (c) 2002 Apple Computer, Inc. All rights reserved.
5 * Copyright (c) 2003 Torrey T. Lyons. All Rights Reserved.
6 *
7 * Permission is hereby granted, free of charge, to any person obtaining a
8 * copy of this software and associated documentation files (the "Software"),
9 * to deal in the Software without restriction, including without limitation
10 * the rights to use, copy, modify, merge, publish, distribute, sublicense,
11 * and/or sell copies of the Software, and to permit persons to whom the
12 * Software is furnished to do so, subject to the following conditions:
13 *
14 * The above copyright notice and this permission notice shall be included in
15 * all copies or substantial portions of the Software.
16 *
17 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
18 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
19 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
20 * THE ABOVE LISTED COPYRIGHT HOLDER(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR
21 * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
22 * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
23 * DEALINGS IN THE SOFTWARE.
24 *
25 * Except as contained in this notice, the name(s) of the above copyright
26 * holders shall not be used in advertising or otherwise to promote the sale,
27 * use or other dealings in this Software without prior written authorization.
28 */
29
30#ifdef HAVE_DIX_CONFIG_H
31#include <dix-config.h>
32#endif
33
34#include "xpr.h"
35#include "rootlessCommon.h"
36#include <Xplugin.h>
37#include "x-hash.h"
38#include "x-list.h"
39#include "applewmExt.h"
40
41#include "propertyst.h"
42#include "dix.h"
43#include <X11/Xatom.h>
44#include "windowstr.h"
45#include "quartz.h"
46
47#include "threadSafety.h"
48
49#include <pthread.h>
50
51#define DEFINE_ATOM_HELPER(func,atom_name)                      \
52static Atom func (void) {                                       \
53    static int generation;                                      \
54    static Atom atom;                                           \
55    if (generation != serverGeneration) {                       \
56        generation = serverGeneration;                          \
57        atom = MakeAtom (atom_name, strlen (atom_name), TRUE);  \
58    }                                                           \
59    return atom;                                                \
60}
61
62DEFINE_ATOM_HELPER(xa_native_window_id, "_NATIVE_WINDOW_ID")
63
64/* Maps xp_window_id -> RootlessWindowRec */
65static x_hash_table *window_hash;
66static pthread_mutex_t window_hash_mutex;
67
68/* Prototypes for static functions */
69static Bool xprCreateFrame(RootlessWindowPtr pFrame, ScreenPtr pScreen,
70               int newX, int newY, RegionPtr pShape);
71static void xprDestroyFrame(RootlessFrameID wid);
72static void xprMoveFrame(RootlessFrameID wid, ScreenPtr pScreen, int newX, int newY);
73static void xprResizeFrame(RootlessFrameID wid, ScreenPtr pScreen,
74               int newX, int newY, unsigned int newW, unsigned int newH,
75               unsigned int gravity);
76static void xprRestackFrame(RootlessFrameID wid, RootlessFrameID nextWid);
77static void xprReshapeFrame(RootlessFrameID wid, RegionPtr pShape);
78static void xprUnmapFrame(RootlessFrameID wid);
79static void xprStartDrawing(RootlessFrameID wid, char **pixelData, int *bytesPerRow);
80static void xprStopDrawing(RootlessFrameID wid, Bool flush);
81static void xprUpdateRegion(RootlessFrameID wid, RegionPtr pDamage);
82static void xprDamageRects(RootlessFrameID wid, int nrects, const BoxRec *rects,
83               int shift_x, int shift_y);
84static void xprSwitchWindow(RootlessWindowPtr pFrame, WindowPtr oldWin);
85static Bool xprDoReorderWindow(RootlessWindowPtr pFrame);
86static void xprHideWindow(RootlessFrameID wid);
87static void xprUpdateColormap(RootlessFrameID wid, ScreenPtr pScreen);
88static void xprCopyWindow(RootlessFrameID wid, int dstNrects, const BoxRec *dstRects,
89              int dx, int dy);
90
91
92static inline xp_error
93xprConfigureWindow(xp_window_id id, unsigned int mask,
94                   const xp_window_changes *values)
95{
96    TA_SERVER();
97
98    return xp_configure_window(id, mask, values);
99}
100
101
102static void
103xprSetNativeProperty(RootlessWindowPtr pFrame)
104{
105    xp_error err;
106    unsigned int native_id;
107    long data;
108
109    TA_SERVER();
110
111    err = xp_get_native_window(x_cvt_vptr_to_uint(pFrame->wid), &native_id);
112    if (err == Success)
113    {
114        /* FIXME: move this to AppleWM extension */
115
116        data = native_id;
117        dixChangeWindowProperty(serverClient, pFrame->win, xa_native_window_id(),
118				XA_INTEGER, 32, PropModeReplace, 1, &data, TRUE);
119    }
120}
121
122static xp_error
123xprColormapCallback(void *data, int first_color, int n_colors, uint32_t *colors)
124{
125    return (RootlessResolveColormap (data, first_color, n_colors, colors) ? XP_Success : XP_BadMatch);
126}
127
128/*
129 * Create and display a new frame.
130 */
131static Bool
132xprCreateFrame(RootlessWindowPtr pFrame, ScreenPtr pScreen,
133               int newX, int newY, RegionPtr pShape)
134{
135    WindowPtr pWin = pFrame->win;
136    xp_window_changes wc;
137    unsigned int mask = 0;
138    xp_error err;
139
140    TA_SERVER();
141
142    wc.x = newX;
143    wc.y = newY;
144    wc.width = pFrame->width;
145    wc.height = pFrame->height;
146    wc.bit_gravity = XP_GRAVITY_NONE;
147    mask |= XP_BOUNDS;
148
149    if (pWin->drawable.depth == 8)
150    {
151        wc.depth = XP_DEPTH_INDEX8;
152        wc.colormap = xprColormapCallback;
153        wc.colormap_data = pScreen;
154        mask |= XP_COLORMAP;
155    }
156    else if (pWin->drawable.depth == 15)
157        wc.depth = XP_DEPTH_RGB555;
158    else if (pWin->drawable.depth == 24)
159        wc.depth = XP_DEPTH_ARGB8888;
160    else
161        wc.depth = XP_DEPTH_NIL;
162    mask |= XP_DEPTH;
163
164    if (pShape != NULL)
165    {
166        wc.shape_nrects = RegionNumRects(pShape);
167        wc.shape_rects = RegionRects(pShape);
168        wc.shape_tx = wc.shape_ty = 0;
169        mask |= XP_SHAPE;
170    }
171
172    pFrame->level = !IsRoot (pWin) ? AppleWMWindowLevelNormal : AppleWMNumWindowLevels;
173
174    if(XQuartzIsRootless)
175        wc.window_level = normal_window_levels[pFrame->level];
176    else if(XQuartzShieldingWindowLevel)
177        wc.window_level = XQuartzShieldingWindowLevel + 1;
178    else
179        wc.window_level = rooted_window_levels[pFrame->level];
180    mask |= XP_WINDOW_LEVEL;
181
182    err = xp_create_window(mask, &wc, (xp_window_id *) &pFrame->wid);
183
184    if (err != Success)
185    {
186        return FALSE;
187    }
188
189    if (window_hash == NULL)
190    {
191        window_hash = x_hash_table_new(NULL, NULL, NULL, NULL);
192        pthread_mutex_init(&window_hash_mutex, NULL);
193    }
194
195    pthread_mutex_lock(&window_hash_mutex);
196    x_hash_table_insert(window_hash, pFrame->wid, pFrame);
197    pthread_mutex_unlock(&window_hash_mutex);
198
199    xprSetNativeProperty(pFrame);
200
201    return TRUE;
202}
203
204
205/*
206 * Destroy a frame.
207 */
208static void
209xprDestroyFrame(RootlessFrameID wid)
210{
211    xp_error err;
212    TA_SERVER();
213
214    pthread_mutex_lock(&window_hash_mutex);
215    x_hash_table_remove(window_hash, wid);
216    pthread_mutex_unlock(&window_hash_mutex);
217
218    err = xp_destroy_window(x_cvt_vptr_to_uint(wid));
219    if (err != Success)
220        FatalError("Could not destroy window %d (%d).", (int)x_cvt_vptr_to_uint(wid), (int)err);
221}
222
223
224/*
225 * Move a frame on screen.
226 */
227static void
228xprMoveFrame(RootlessFrameID wid, ScreenPtr pScreen, int newX, int newY)
229{
230    xp_window_changes wc;
231
232    TA_SERVER();
233
234    wc.x = newX;
235    wc.y = newY;
236    //    ErrorF("xprMoveFrame(%d, %p, %d, %d)\n", wid, pScreen, newX, newY);
237    xprConfigureWindow(x_cvt_vptr_to_uint(wid), XP_ORIGIN, &wc);
238}
239
240
241/*
242 * Resize and move a frame.
243 */
244static void
245xprResizeFrame(RootlessFrameID wid, ScreenPtr pScreen,
246               int newX, int newY, unsigned int newW, unsigned int newH,
247               unsigned int gravity)
248{
249    xp_window_changes wc;
250
251    TA_SERVER();
252
253    wc.x = newX;
254    wc.y = newY;
255    wc.width = newW;
256    wc.height = newH;
257    wc.bit_gravity = gravity;
258
259    /* It's unlikely that being async will save us anything here.
260       But it can't hurt. */
261
262    xprConfigureWindow(x_cvt_vptr_to_uint(wid), XP_BOUNDS, &wc);
263}
264
265
266/*
267 * Change frame stacking.
268 */
269static void xprRestackFrame(RootlessFrameID wid, RootlessFrameID nextWid) {
270    xp_window_changes wc;
271    unsigned int mask = XP_STACKING;
272
273    TA_SERVER();
274
275    /* Stack frame below nextWid it if it exists, or raise
276       frame above everything otherwise. */
277
278    if(nextWid == NULL) {
279        wc.stack_mode = XP_MAPPED_ABOVE;
280        wc.sibling = 0;
281    } else {
282        wc.stack_mode = XP_MAPPED_BELOW;
283        wc.sibling = x_cvt_vptr_to_uint(nextWid);
284    }
285
286    if(window_hash) {
287        RootlessWindowRec *winRec = x_hash_table_lookup(window_hash, wid, NULL);
288
289        if(winRec) {
290            if(XQuartzIsRootless)
291                wc.window_level = normal_window_levels[winRec->level];
292            else if(XQuartzShieldingWindowLevel)
293                wc.window_level = XQuartzShieldingWindowLevel + 1;
294            else
295                wc.window_level = rooted_window_levels[winRec->level];
296            mask |= XP_WINDOW_LEVEL;
297        }
298    }
299
300    xprConfigureWindow(x_cvt_vptr_to_uint(wid), mask, &wc);
301}
302
303
304/*
305 * Change the frame's shape.
306 */
307static void
308xprReshapeFrame(RootlessFrameID wid, RegionPtr pShape)
309{
310    xp_window_changes wc;
311
312    TA_SERVER();
313
314    if (pShape != NULL)
315    {
316        wc.shape_nrects = RegionNumRects(pShape);
317        wc.shape_rects = RegionRects(pShape);
318    }
319    else
320    {
321        wc.shape_nrects = -1;
322        wc.shape_rects = NULL;
323    }
324
325    wc.shape_tx = wc.shape_ty = 0;
326
327    xprConfigureWindow(x_cvt_vptr_to_uint(wid), XP_SHAPE, &wc);
328}
329
330
331/*
332 * Unmap a frame.
333 */
334static void
335xprUnmapFrame(RootlessFrameID wid)
336{
337    xp_window_changes wc;
338
339    TA_SERVER();
340
341    wc.stack_mode = XP_UNMAPPED;
342    wc.sibling = 0;
343
344    xprConfigureWindow(x_cvt_vptr_to_uint(wid), XP_STACKING, &wc);
345}
346
347
348/*
349 * Start drawing to a frame.
350 *  Prepare for direct access to its backing buffer.
351 */
352static void
353xprStartDrawing(RootlessFrameID wid, char **pixelData, int *bytesPerRow)
354{
355    void *data[2];
356    unsigned int rowbytes[2];
357    xp_error err;
358
359    TA_SERVER();
360
361    err = xp_lock_window(x_cvt_vptr_to_uint(wid), NULL, NULL, data, rowbytes, NULL);
362    if (err != Success)
363        FatalError("Could not lock window %d for drawing (%d).", (int)x_cvt_vptr_to_uint(wid), (int)err);
364
365    *pixelData = data[0];
366    *bytesPerRow = rowbytes[0];
367}
368
369
370/*
371 * Stop drawing to a frame.
372 */
373static void
374xprStopDrawing(RootlessFrameID wid, Bool flush)
375{
376    xp_error err;
377    TA_SERVER();
378
379    err = xp_unlock_window(x_cvt_vptr_to_uint(wid), flush);
380    /* This should be a FatalError, but we started tripping over it.  Make it a
381     * FatalError after http://xquartz.macosforge.org/trac/ticket/482 is fixed.
382     */
383    if(err != Success)
384        ErrorF("Could not unlock window %d after drawing (%d).", (int)x_cvt_vptr_to_uint(wid), (int)err);
385}
386
387
388/*
389 * Flush drawing updates to the screen.
390 */
391static void
392xprUpdateRegion(RootlessFrameID wid, RegionPtr pDamage)
393{
394    TA_SERVER();
395
396    xp_flush_window(x_cvt_vptr_to_uint(wid));
397}
398
399
400/*
401 * Mark damaged rectangles as requiring redisplay to screen.
402 */
403static void
404xprDamageRects(RootlessFrameID wid, int nrects, const BoxRec *rects,
405               int shift_x, int shift_y)
406{
407    TA_SERVER();
408
409    xp_mark_window(x_cvt_vptr_to_uint(wid), nrects, rects, shift_x, shift_y);
410}
411
412
413/*
414 * Called after the window associated with a frame has been switched
415 * to a new top-level parent.
416 */
417static void
418xprSwitchWindow(RootlessWindowPtr pFrame, WindowPtr oldWin)
419{
420    DeleteProperty(serverClient, oldWin, xa_native_window_id());
421
422    TA_SERVER();
423
424    xprSetNativeProperty(pFrame);
425}
426
427
428/*
429 * Called to check if the frame should be reordered when it is restacked.
430 */
431static Bool xprDoReorderWindow(RootlessWindowPtr pFrame)
432{
433    WindowPtr pWin = pFrame->win;
434
435    TA_SERVER();
436
437    return AppleWMDoReorderWindow(pWin);
438}
439
440
441/*
442 * Copy area in frame to another part of frame.
443 *  Used to accelerate scrolling.
444 */
445static void
446xprCopyWindow(RootlessFrameID wid, int dstNrects, const BoxRec *dstRects,
447              int dx, int dy)
448{
449    TA_SERVER();
450
451    xp_copy_window(x_cvt_vptr_to_uint(wid), x_cvt_vptr_to_uint(wid),
452                   dstNrects, dstRects, dx, dy);
453}
454
455
456static RootlessFrameProcsRec xprRootlessProcs = {
457    xprCreateFrame,
458    xprDestroyFrame,
459    xprMoveFrame,
460    xprResizeFrame,
461    xprRestackFrame,
462    xprReshapeFrame,
463    xprUnmapFrame,
464    xprStartDrawing,
465    xprStopDrawing,
466    xprUpdateRegion,
467    xprDamageRects,
468    xprSwitchWindow,
469    xprDoReorderWindow,
470    xprHideWindow,
471    xprUpdateColormap,
472    xp_copy_bytes,
473    xprCopyWindow
474};
475
476
477/*
478 * Initialize XPR implementation
479 */
480Bool
481xprInit(ScreenPtr pScreen)
482{
483    RootlessInit(pScreen, &xprRootlessProcs);
484
485    TA_SERVER();
486
487    rootless_CopyBytes_threshold = xp_copy_bytes_threshold;
488    rootless_CopyWindow_threshold = xp_scroll_area_threshold;
489
490    return TRUE;
491}
492
493
494/*
495 * Given the id of a physical window, try to find the top-level (or root)
496 * X window that it represents.
497 */
498WindowPtr
499xprGetXWindow(xp_window_id wid)
500{
501    RootlessWindowRec *winRec;
502
503    if (window_hash == NULL)
504        return NULL;
505
506    winRec = x_hash_table_lookup(window_hash, x_cvt_uint_to_vptr(wid), NULL);
507
508    return winRec != NULL ? winRec->win : NULL;
509}
510
511#ifdef UNUSED_CODE
512/*
513 * Given the id of a physical window, try to find the top-level (or root)
514 * X window that it represents.
515 */
516WindowPtr
517xprGetXWindowFromAppKit(int windowNumber)
518{
519    RootlessWindowRec *winRec;
520    Bool ret;
521    xp_window_id wid;
522
523    if (window_hash == NULL)
524        return FALSE;
525
526    /* need to lock, since this function can be called by any thread */
527
528    pthread_mutex_lock(&window_hash_mutex);
529
530    if (xp_lookup_native_window(windowNumber, &wid))
531        ret = xprGetXWindow(wid) != NULL;
532    else
533        ret = FALSE;
534
535    pthread_mutex_unlock(&window_hash_mutex);
536
537    if (!ret) return NULL;
538    winRec = x_hash_table_lookup(window_hash, x_cvt_uint_to_vptr(wid), NULL);
539
540    return winRec != NULL ? winRec->win : NULL;
541}
542#endif
543
544/*
545 * The windowNumber is an AppKit window number. Returns TRUE if xpr is
546 * displaying a window with that number.
547 */
548Bool
549xprIsX11Window(void *nsWindow, int windowNumber)
550{
551    Bool ret;
552    xp_window_id wid;
553
554    if (window_hash == NULL)
555        return FALSE;
556
557    /* need to lock, since this function can be called by any thread */
558
559    pthread_mutex_lock(&window_hash_mutex);
560
561    if (xp_lookup_native_window(windowNumber, &wid))
562        ret = xprGetXWindow(wid) != NULL;
563    else
564        ret = FALSE;
565
566    pthread_mutex_unlock(&window_hash_mutex);
567
568    return ret;
569}
570
571
572/*
573 * xprHideWindows
574 *  Hide or unhide all top level windows. This is called for application hide/
575 *  unhide events if the window manager is not Apple-WM aware. Xplugin windows
576 *  do not hide or unhide themselves.
577 */
578void
579xprHideWindows(Bool hide)
580{
581    int screen;
582    WindowPtr pRoot, pWin;
583
584    TA_SERVER();
585
586    for (screen = 0; screen < screenInfo.numScreens; screen++) {
587        RootlessFrameID prevWid = NULL;
588        pRoot = screenInfo.screens[screen]->root;
589
590        for (pWin = pRoot->firstChild; pWin; pWin = pWin->nextSib) {
591            RootlessWindowRec *winRec = WINREC(pWin);
592
593            if (winRec != NULL) {
594                if (hide) {
595                    xprUnmapFrame(winRec->wid);
596                } else {
597                    BoxRec box;
598
599                    xprRestackFrame(winRec->wid, prevWid);
600                    prevWid = winRec->wid;
601
602                    box.x1 = 0;
603                    box.y1 = 0;
604                    box.x2 = winRec->width;
605                    box.y2 = winRec->height;
606
607                    xprDamageRects(winRec->wid, 1, &box, 0, 0);
608                    RootlessQueueRedisplay(screenInfo.screens[screen]);
609                }
610            }
611        }
612    }
613}
614
615// XXX: identical to x_cvt_vptr_to_uint ?
616#define MAKE_WINDOW_ID(x)		((xp_window_id)((size_t)(x)))
617
618Bool no_configure_window;
619
620static inline int
621configure_window (xp_window_id id, unsigned int mask,
622                  const xp_window_changes *values)
623{
624  if (!no_configure_window)
625    return xp_configure_window (id, mask, values);
626  else
627    return XP_Success;
628}
629
630
631static
632void xprUpdateColormap(RootlessFrameID wid, ScreenPtr pScreen)
633{
634  /* This is how we tell xp that the colormap may have changed. */
635  xp_window_changes wc;
636  wc.colormap = xprColormapCallback;
637  wc.colormap_data = pScreen;
638
639  configure_window(MAKE_WINDOW_ID(wid), XP_COLORMAP, &wc);
640}
641
642static
643void xprHideWindow(RootlessFrameID wid)
644{
645  xp_window_changes wc;
646  wc.stack_mode = XP_UNMAPPED;
647  wc.sibling = 0;
648  configure_window(MAKE_WINDOW_ID(wid), XP_STACKING, &wc);
649}
650