applewm.c revision 35c4bbdf
1/*
2 * Copyright (c) 2003 Torrey T. Lyons. All Rights Reserved.
3 * Copyright (c) 2002-2012 Apple Inc. All rights reserved.
4 *
5 * Permission is hereby granted, free of charge, to any person
6 * obtaining a copy of this software and associated documentation files
7 * (the "Software"), to deal in the Software without restriction,
8 * including without limitation the rights to use, copy, modify, merge,
9 * publish, distribute, sublicense, and/or sell copies of the Software,
10 * and to permit persons to whom the Software is furnished to do so,
11 * subject to the following conditions:
12 *
13 * The above copyright notice and this permission notice shall be
14 * included in all copies or substantial portions of the Software.
15 *
16 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
17 * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
18 * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
19 * NONINFRINGEMENT.  IN NO EVENT SHALL THE ABOVE LISTED COPYRIGHT
20 * HOLDER(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
21 * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
22 * 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
26 * copyright holders shall not be used in advertising or otherwise to
27 * promote the sale, use or other dealings in this Software without
28 * prior written authorization.
29 */
30
31#include "sanitizedCarbon.h"
32
33#ifdef HAVE_DIX_CONFIG_H
34#include <dix-config.h>
35#endif
36
37#include "quartzCommon.h"
38
39#include "misc.h"
40#include "dixstruct.h"
41#include "globals.h"
42#include "extnsionst.h"
43#include "colormapst.h"
44#include "cursorstr.h"
45#include "scrnintstr.h"
46#include "windowstr.h"
47#include "servermd.h"
48#include "swaprep.h"
49#include "propertyst.h"
50#include <X11/Xatom.h>
51#include "darwin.h"
52#define _APPLEWM_SERVER_
53#include <X11/extensions/applewmproto.h>
54#include "applewmExt.h"
55#include "X11Application.h"
56#include "protocol-versions.h"
57
58#define DEFINE_ATOM_HELPER(func, atom_name)                      \
59    static Atom func(void) {                                       \
60        static int generation;                                      \
61        static Atom atom;                                           \
62        if (generation != serverGeneration) {                       \
63            generation = serverGeneration;                          \
64            atom = MakeAtom(atom_name, strlen(atom_name), TRUE);  \
65        }                                                           \
66        return atom;                                                \
67    }
68
69DEFINE_ATOM_HELPER(xa_native_screen_origin, "_NATIVE_SCREEN_ORIGIN")
70DEFINE_ATOM_HELPER(xa_apple_no_order_in, "_APPLE_NO_ORDER_IN")
71
72static AppleWMProcsPtr appleWMProcs;
73
74static int WMErrorBase;
75
76static unsigned char WMReqCode = 0;
77static int WMEventBase = 0;
78
79static RESTYPE ClientType, EventType; /* resource types for event masks */
80static XID eventResource;
81
82/* Currently selected events */
83static unsigned int eventMask = 0;
84
85static int
86WMFreeClient(void *data, XID id);
87static int
88WMFreeEvents(void *data, XID id);
89static void
90SNotifyEvent(xAppleWMNotifyEvent *from, xAppleWMNotifyEvent *to);
91
92typedef struct _WMEvent *WMEventPtr;
93typedef struct _WMEvent {
94    WMEventPtr next;
95    ClientPtr client;
96    XID clientResource;
97    unsigned int mask;
98} WMEventRec;
99
100static inline BoxRec
101make_box(int x, int y, int w, int h)
102{
103    BoxRec r;
104    r.x1 = x;
105    r.y1 = y;
106    r.x2 = x + w;
107    r.y2 = y + h;
108    return r;
109}
110
111/* Updates the _NATIVE_SCREEN_ORIGIN property on the given root window. */
112void
113AppleWMSetScreenOrigin(WindowPtr pWin)
114{
115    int32_t data[2];
116
117    data[0] = pWin->drawable.pScreen->x + darwinMainScreenX;
118    data[1] = pWin->drawable.pScreen->y + darwinMainScreenY;
119
120    dixChangeWindowProperty(serverClient, pWin, xa_native_screen_origin(),
121                            XA_INTEGER, 32, PropModeReplace, 2, data, TRUE);
122}
123
124/* Window managers can set the _APPLE_NO_ORDER_IN property on windows
125   that are being genie-restored from the Dock. We want them to
126   be mapped but remain ordered-out until the animation
127   completes (when the Dock will order them in). */
128Bool
129AppleWMDoReorderWindow(WindowPtr pWin)
130{
131    Atom atom;
132    PropertyPtr prop;
133    int rc;
134
135    atom = xa_apple_no_order_in();
136    rc = dixLookupProperty(&prop, pWin, atom, serverClient, DixReadAccess);
137
138    if (Success == rc && prop->type == atom)
139        return 0;
140
141    return 1;
142}
143
144static int
145ProcAppleWMQueryVersion(register ClientPtr client)
146{
147    xAppleWMQueryVersionReply rep;
148
149    REQUEST_SIZE_MATCH(xAppleWMQueryVersionReq);
150    rep.type = X_Reply;
151    rep.length = 0;
152    rep.sequenceNumber = client->sequence;
153    rep.majorVersion = SERVER_APPLEWM_MAJOR_VERSION;
154    rep.minorVersion = SERVER_APPLEWM_MINOR_VERSION;
155    rep.patchVersion = SERVER_APPLEWM_PATCH_VERSION;
156    if (client->swapped) {
157        swaps(&rep.sequenceNumber);
158        swapl(&rep.length);
159    }
160    WriteToClient(client, sizeof(xAppleWMQueryVersionReply),&rep);
161    return Success;
162}
163
164/* events */
165
166static inline void
167updateEventMask(WMEventPtr *pHead)
168{
169    WMEventPtr pCur;
170
171    eventMask = 0;
172    for (pCur = *pHead; pCur != NULL; pCur = pCur->next)
173        eventMask |= pCur->mask;
174}
175
176/*ARGSUSED*/
177static int
178WMFreeClient(void *data, XID id)
179{
180    WMEventPtr pEvent;
181    WMEventPtr   *pHead, pCur, pPrev;
182    int i;
183
184    pEvent = (WMEventPtr)data;
185    i = dixLookupResourceByType(
186        (void **)&pHead, eventResource, EventType, serverClient,
187        DixReadAccess |
188        DixWriteAccess | DixDestroyAccess);
189    if (i == Success && pHead) {
190        pPrev = 0;
191        for (pCur = *pHead; pCur && pCur != pEvent; pCur = pCur->next)
192            pPrev = pCur;
193        if (pCur) {
194            if (pPrev)
195                pPrev->next = pEvent->next;
196            else
197                *pHead = pEvent->next;
198        }
199        updateEventMask(pHead);
200    }
201    free((void *)pEvent);
202    return 1;
203}
204
205/*ARGSUSED*/
206static int
207WMFreeEvents(void *data, XID id)
208{
209    WMEventPtr   *pHead, pCur, pNext;
210
211    pHead = (WMEventPtr *)data;
212    for (pCur = *pHead; pCur; pCur = pNext) {
213        pNext = pCur->next;
214        FreeResource(pCur->clientResource, ClientType);
215        free((void *)pCur);
216    }
217    free((void *)pHead);
218    eventMask = 0;
219    return 1;
220}
221
222static int
223ProcAppleWMSelectInput(register ClientPtr client)
224{
225    REQUEST(xAppleWMSelectInputReq);
226    WMEventPtr pEvent, pNewEvent, *pHead;
227    XID clientResource;
228    int i;
229
230    REQUEST_SIZE_MATCH(xAppleWMSelectInputReq);
231    i =
232        dixLookupResourceByType((void **)&pHead, eventResource, EventType,
233                                client,
234                                DixWriteAccess);
235    if (stuff->mask != 0) {
236        if (i == Success && pHead) {
237            /* check for existing entry. */
238            for (pEvent = *pHead; pEvent; pEvent = pEvent->next) {
239                if (pEvent->client == client) {
240                    pEvent->mask = stuff->mask;
241                    updateEventMask(pHead);
242                    return Success;
243                }
244            }
245        }
246
247        /* build the entry */
248        pNewEvent = (WMEventPtr)malloc(sizeof(WMEventRec));
249        if (!pNewEvent)
250            return BadAlloc;
251        pNewEvent->next = 0;
252        pNewEvent->client = client;
253        pNewEvent->mask = stuff->mask;
254        /*
255         * add a resource that will be deleted when
256         * the client goes away
257         */
258        clientResource = FakeClientID(client->index);
259        pNewEvent->clientResource = clientResource;
260        if (!AddResource(clientResource, ClientType, (void *)pNewEvent))
261            return BadAlloc;
262        /*
263         * create a resource to contain a pointer to the list
264         * of clients selecting input.  This must be indirect as
265         * the list may be arbitrarily rearranged which cannot be
266         * done through the resource database.
267         */
268        if (i != Success || !pHead) {
269            pHead = (WMEventPtr *)malloc(sizeof(WMEventPtr));
270            if (!pHead ||
271                !AddResource(eventResource, EventType, (void *)pHead)) {
272                FreeResource(clientResource, RT_NONE);
273                return BadAlloc;
274            }
275            *pHead = 0;
276        }
277        pNewEvent->next = *pHead;
278        *pHead = pNewEvent;
279        updateEventMask(pHead);
280    }
281    else if (stuff->mask == 0) {
282        /* delete the interest */
283        if (i == Success && pHead) {
284            pNewEvent = 0;
285            for (pEvent = *pHead; pEvent; pEvent = pEvent->next) {
286                if (pEvent->client == client)
287                    break;
288                pNewEvent = pEvent;
289            }
290            if (pEvent) {
291                FreeResource(pEvent->clientResource, ClientType);
292                if (pNewEvent)
293                    pNewEvent->next = pEvent->next;
294                else
295                    *pHead = pEvent->next;
296                free(pEvent);
297                updateEventMask(pHead);
298            }
299        }
300    }
301    else {
302        client->errorValue = stuff->mask;
303        return BadValue;
304    }
305    return Success;
306}
307
308/*
309 * deliver the event
310 */
311
312void
313AppleWMSendEvent(int type, unsigned int mask, int which, int arg)
314{
315    WMEventPtr      *pHead, pEvent;
316    xAppleWMNotifyEvent se;
317    int i;
318
319    i =
320        dixLookupResourceByType((void **)&pHead, eventResource, EventType,
321                                serverClient,
322                                DixReadAccess);
323    if (i != Success || !pHead)
324        return;
325    for (pEvent = *pHead; pEvent; pEvent = pEvent->next) {
326        if ((pEvent->mask & mask) == 0)
327            continue;
328        se.type = type + WMEventBase;
329        se.kind = which;
330        se.arg = arg;
331        se.time = currentTime.milliseconds;
332        WriteEventsToClient(pEvent->client, 1, (xEvent *)&se);
333    }
334}
335
336/* Safe to call from any thread. */
337unsigned int
338AppleWMSelectedEvents(void)
339{
340    return eventMask;
341}
342
343/* general utility functions */
344
345static int
346ProcAppleWMDisableUpdate(register ClientPtr client)
347{
348    REQUEST_SIZE_MATCH(xAppleWMDisableUpdateReq);
349
350    appleWMProcs->DisableUpdate();
351
352    return Success;
353}
354
355static int
356ProcAppleWMReenableUpdate(register ClientPtr client)
357{
358    REQUEST_SIZE_MATCH(xAppleWMReenableUpdateReq);
359
360    appleWMProcs->EnableUpdate();
361
362    return Success;
363}
364
365/* window functions */
366
367static int
368ProcAppleWMSetWindowMenu(register ClientPtr client)
369{
370    const char *bytes, **items;
371    char *shortcuts;
372    int max_len, nitems, i, j;
373    REQUEST(xAppleWMSetWindowMenuReq);
374
375    REQUEST_AT_LEAST_SIZE(xAppleWMSetWindowMenuReq);
376
377    nitems = stuff->nitems;
378    items = malloc(sizeof(char *) * nitems);
379    shortcuts = malloc(sizeof(char) * nitems);
380
381    if (!items || !shortcuts) {
382        free(items);
383        free(shortcuts);
384
385        return BadAlloc;
386    }
387
388    max_len = (stuff->length << 2) - sizeof(xAppleWMSetWindowMenuReq);
389    bytes = (char *)&stuff[1];
390
391    for (i = j = 0; i < max_len && j < nitems;) {
392        shortcuts[j] = bytes[i++];
393        items[j++] = bytes + i;
394
395        while (i < max_len)
396        {
397            if (bytes[i++] == 0)
398                break;
399        }
400    }
401
402    /* Check if we bailed out of the above loop due to a request that was too long */
403    if (j < nitems) {
404        free(items);
405        free(shortcuts);
406
407        return BadRequest;
408    }
409
410    X11ApplicationSetWindowMenu(nitems, items, shortcuts);
411    free(items);
412    free(shortcuts);
413
414    return Success;
415}
416
417static int
418ProcAppleWMSetWindowMenuCheck(register ClientPtr client)
419{
420    REQUEST(xAppleWMSetWindowMenuCheckReq);
421
422    REQUEST_SIZE_MATCH(xAppleWMSetWindowMenuCheckReq);
423    X11ApplicationSetWindowMenuCheck(stuff->index);
424    return Success;
425}
426
427static int
428ProcAppleWMSetFrontProcess(register ClientPtr client)
429{
430    REQUEST_SIZE_MATCH(xAppleWMSetFrontProcessReq);
431
432    X11ApplicationSetFrontProcess();
433    return Success;
434}
435
436static int
437ProcAppleWMSetWindowLevel(register ClientPtr client)
438{
439    REQUEST(xAppleWMSetWindowLevelReq);
440    WindowPtr pWin;
441    int err;
442
443    REQUEST_SIZE_MATCH(xAppleWMSetWindowLevelReq);
444
445    if (Success != dixLookupWindow(&pWin, stuff->window, client,
446                                   DixReadAccess))
447        return BadValue;
448
449    if (stuff->level >= AppleWMNumWindowLevels) {
450        return BadValue;
451    }
452
453    err = appleWMProcs->SetWindowLevel(pWin, stuff->level);
454    if (err != Success) {
455        return err;
456    }
457
458    return Success;
459}
460
461static int
462ProcAppleWMSendPSN(register ClientPtr client)
463{
464    REQUEST(xAppleWMSendPSNReq);
465    int err;
466
467    REQUEST_SIZE_MATCH(xAppleWMSendPSNReq);
468
469    if (!appleWMProcs->SendPSN)
470        return BadRequest;
471
472    err = appleWMProcs->SendPSN(stuff->psn_hi, stuff->psn_lo);
473    if (err != Success) {
474        return err;
475    }
476
477    return Success;
478}
479
480static int
481ProcAppleWMAttachTransient(register ClientPtr client)
482{
483    WindowPtr pWinChild, pWinParent;
484    REQUEST(xAppleWMAttachTransientReq);
485    int err;
486
487    REQUEST_SIZE_MATCH(xAppleWMAttachTransientReq);
488
489    if (!appleWMProcs->AttachTransient)
490        return BadRequest;
491
492    if (Success !=
493        dixLookupWindow(&pWinChild, stuff->child, client, DixReadAccess))
494        return BadValue;
495
496    if (stuff->parent) {
497        if (Success !=
498            dixLookupWindow(&pWinParent, stuff->parent, client, DixReadAccess))
499            return BadValue;
500    }
501    else {
502        pWinParent = NULL;
503    }
504
505    err = appleWMProcs->AttachTransient(pWinChild, pWinParent);
506    if (err != Success) {
507        return err;
508    }
509
510    return Success;
511}
512
513static int
514ProcAppleWMSetCanQuit(register ClientPtr client)
515{
516    REQUEST(xAppleWMSetCanQuitReq);
517
518    REQUEST_SIZE_MATCH(xAppleWMSetCanQuitReq);
519
520    X11ApplicationSetCanQuit(stuff->state);
521    return Success;
522}
523
524/* frame functions */
525
526static int
527ProcAppleWMFrameGetRect(register ClientPtr client)
528{
529    xAppleWMFrameGetRectReply rep;
530    BoxRec ir, or, rr;
531    REQUEST(xAppleWMFrameGetRectReq);
532
533    REQUEST_SIZE_MATCH(xAppleWMFrameGetRectReq);
534    rep.type = X_Reply;
535    rep.length = 0;
536    rep.sequenceNumber = client->sequence;
537
538    ir = make_box(stuff->ix, stuff->iy, stuff->iw, stuff->ih);
539    or = make_box(stuff->ox, stuff->oy, stuff->ow, stuff->oh);
540
541    if (appleWMProcs->FrameGetRect(stuff->frame_rect,
542                                   stuff->frame_class,
543                                   &or, &ir, &rr) != Success) {
544        return BadValue;
545    }
546
547    rep.x = rr.x1;
548    rep.y = rr.y1;
549    rep.w = rr.x2 - rr.x1;
550    rep.h = rr.y2 - rr.y1;
551
552    WriteToClient(client, sizeof(xAppleWMFrameGetRectReply),&rep);
553    return Success;
554}
555
556static int
557ProcAppleWMFrameHitTest(register ClientPtr client)
558{
559    xAppleWMFrameHitTestReply rep;
560    BoxRec ir, or;
561    int ret;
562    REQUEST(xAppleWMFrameHitTestReq);
563
564    REQUEST_SIZE_MATCH(xAppleWMFrameHitTestReq);
565    rep.type = X_Reply;
566    rep.length = 0;
567    rep.sequenceNumber = client->sequence;
568
569    ir = make_box(stuff->ix, stuff->iy, stuff->iw, stuff->ih);
570    or = make_box(stuff->ox, stuff->oy, stuff->ow, stuff->oh);
571
572    if (appleWMProcs->FrameHitTest(stuff->frame_class, stuff->px,
573                                   stuff->py, &or, &ir, &ret) != Success) {
574        return BadValue;
575    }
576
577    rep.ret = ret;
578
579    WriteToClient(client, sizeof(xAppleWMFrameHitTestReply),&rep);
580    return Success;
581}
582
583static int
584ProcAppleWMFrameDraw(register ClientPtr client)
585{
586    BoxRec ir, or;
587    unsigned int title_length, title_max;
588    unsigned char *title_bytes;
589    REQUEST(xAppleWMFrameDrawReq);
590    WindowPtr pWin;
591
592    REQUEST_AT_LEAST_SIZE(xAppleWMFrameDrawReq);
593
594    if (Success != dixLookupWindow(&pWin, stuff->window, client,
595                                   DixReadAccess))
596        return BadValue;
597
598    ir = make_box(stuff->ix, stuff->iy, stuff->iw, stuff->ih);
599    or = make_box(stuff->ox, stuff->oy, stuff->ow, stuff->oh);
600
601    title_length = stuff->title_length;
602    title_max = (stuff->length << 2) - sizeof(xAppleWMFrameDrawReq);
603
604    if (title_max < title_length)
605        return BadValue;
606
607    title_bytes = (unsigned char *)&stuff[1];
608
609    errno = appleWMProcs->FrameDraw(pWin, stuff->frame_class,
610                                    stuff->frame_attr, &or, &ir,
611                                    title_length, title_bytes);
612    if (errno != Success) {
613        return errno;
614    }
615
616    return Success;
617}
618
619/* dispatch */
620
621static int
622ProcAppleWMDispatch(register ClientPtr client)
623{
624    REQUEST(xReq);
625
626    switch (stuff->data) {
627    case X_AppleWMQueryVersion:
628        return ProcAppleWMQueryVersion(client);
629    }
630
631    if (!client->local)
632        return WMErrorBase + AppleWMClientNotLocal;
633
634    switch (stuff->data) {
635    case X_AppleWMSelectInput:
636        return ProcAppleWMSelectInput(client);
637
638    case X_AppleWMDisableUpdate:
639        return ProcAppleWMDisableUpdate(client);
640
641    case X_AppleWMReenableUpdate:
642        return ProcAppleWMReenableUpdate(client);
643
644    case X_AppleWMSetWindowMenu:
645        return ProcAppleWMSetWindowMenu(client);
646
647    case X_AppleWMSetWindowMenuCheck:
648        return ProcAppleWMSetWindowMenuCheck(client);
649
650    case X_AppleWMSetFrontProcess:
651        return ProcAppleWMSetFrontProcess(client);
652
653    case X_AppleWMSetWindowLevel:
654        return ProcAppleWMSetWindowLevel(client);
655
656    case X_AppleWMSetCanQuit:
657        return ProcAppleWMSetCanQuit(client);
658
659    case X_AppleWMFrameGetRect:
660        return ProcAppleWMFrameGetRect(client);
661
662    case X_AppleWMFrameHitTest:
663        return ProcAppleWMFrameHitTest(client);
664
665    case X_AppleWMFrameDraw:
666        return ProcAppleWMFrameDraw(client);
667
668    case X_AppleWMSendPSN:
669        return ProcAppleWMSendPSN(client);
670
671    case X_AppleWMAttachTransient:
672        return ProcAppleWMAttachTransient(client);
673
674    default:
675        return BadRequest;
676    }
677}
678
679static void
680SNotifyEvent(xAppleWMNotifyEvent *from, xAppleWMNotifyEvent *to)
681{
682    to->type = from->type;
683    to->kind = from->kind;
684    cpswaps(from->sequenceNumber, to->sequenceNumber);
685    cpswapl(from->time, to->time);
686    cpswapl(from->arg, to->arg);
687}
688
689static int
690SProcAppleWMQueryVersion(register ClientPtr client)
691{
692    REQUEST(xAppleWMQueryVersionReq);
693    swaps(&stuff->length);
694    return ProcAppleWMQueryVersion(client);
695}
696
697static int
698SProcAppleWMDispatch(register ClientPtr client)
699{
700    REQUEST(xReq);
701
702    /* It is bound to be non-local when there is byte swapping */
703    if (!client->local)
704        return WMErrorBase + AppleWMClientNotLocal;
705
706    /* only local clients are allowed WM access */
707    switch (stuff->data) {
708    case X_AppleWMQueryVersion:
709        return SProcAppleWMQueryVersion(client);
710
711    default:
712        return BadRequest;
713    }
714}
715
716void
717AppleWMExtensionInit(AppleWMProcsPtr procsPtr)
718{
719    ExtensionEntry* extEntry;
720
721    ClientType = CreateNewResourceType(WMFreeClient, "WMClient");
722    EventType = CreateNewResourceType(WMFreeEvents, "WMEvent");
723    eventResource = FakeClientID(0);
724
725    if (ClientType && EventType &&
726        (extEntry = AddExtension(APPLEWMNAME,
727                                 AppleWMNumberEvents,
728                                 AppleWMNumberErrors,
729                                 ProcAppleWMDispatch,
730                                 SProcAppleWMDispatch,
731                                 NULL,
732                                 StandardMinorOpcode))) {
733        size_t i;
734        WMReqCode = (unsigned char)extEntry->base;
735        WMErrorBase = extEntry->errorBase;
736        WMEventBase = extEntry->eventBase;
737        for (i = 0; i < AppleWMNumberEvents; i++)
738            EventSwapVector[WMEventBase + i] = (EventSwapPtr)SNotifyEvent;
739        appleWMProcs = procsPtr;
740    }
741}
742