2
* Rootless implementation for Mac OS X Aqua environment
5
* Copyright (c) 2001 Greg Parker. All Rights Reserved.
6
* Copyright (c) 2002 Torrey T. Lyons. All Rights Reserved.
8
* Permission is hereby granted, free of charge, to any person obtaining a
9
* copy of this software and associated documentation files (the "Software"),
10
* to deal in the Software without restriction, including without limitation
11
* the rights to use, copy, modify, merge, publish, distribute, sublicense,
12
* and/or sell copies of the Software, and to permit persons to whom the
13
* Software is furnished to do so, subject to the following conditions:
15
* The above copyright notice and this permission notice shall be included in
16
* all copies or substantial portions of the Software.
18
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
19
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
20
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
21
* THE ABOVE LISTED COPYRIGHT HOLDER(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR
22
* OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
23
* ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
24
* DEALINGS IN THE SOFTWARE.
26
* Except as contained in this notice, the name(s) of the above copyright
27
* holders shall not be used in advertising or otherwise to promote the sale,
28
* use or other dealings in this Software without prior written authorization.
30
/* $XFree86: xc/programs/Xserver/hw/darwin/quartz/rootlessAquaImp.m,v 1.6 2003/01/24 00:11:39 torrey Exp $ */
32
#include "rootlessAquaImp.h"
33
#include "fakeBoxRec.h"
34
#include "quartzCommon.h"
35
#include "aquaCommon.h"
36
#include "pseudoramiX.h"
37
#import <Cocoa/Cocoa.h>
38
#include <ApplicationServices/ApplicationServices.h>
41
extern void ErrorF(const char *, ...);
46
* Return the number of displays.
47
* Multihead note: When rootless mode uses PseudoramiX, the
48
* X server only sees one screen; only PseudoramiX itself knows
49
* about all of the screens.
51
int AquaDisplayCount()
53
aquaNumScreens = [[NSScreen screens] count];
55
if (noPseudoramiXExtension) {
56
return aquaNumScreens;
58
return 1; // only PseudoramiX knows about the rest
63
void AquaScreenInit(int index, int *x, int *y, int *width, int *height,
64
int *rowBytes, int *bps, int *spp, int *bpp)
67
*bps = CGDisplayBitsPerSample(kCGDirectMainDisplay);
68
*bpp = CGDisplayBitsPerPixel(kCGDirectMainDisplay);
70
if (noPseudoramiXExtension) {
71
NSScreen *screen = [[NSScreen screens] objectAtIndex:index];
72
NSRect frame = [screen frame];
74
// set x, y so (0,0) is top left of main screen
76
*y = NSHeight([[NSScreen mainScreen] frame]) - NSHeight(frame) -
79
*width = NSWidth(frame);
80
*height = NSHeight(frame);
81
*rowBytes = (*width) * (*bpp) / 8;
83
// Shift the usable part of main screen down to avoid the menu bar.
84
if (NSEqualRects(frame, [[NSScreen mainScreen] frame])) {
85
*y += aquaMenuBarHeight;
86
*height -= aquaMenuBarHeight;
91
NSRect unionRect = NSMakeRect(0, 0, 0, 0);
92
NSArray *screens = [NSScreen screens];
94
// Get the union of all screens (minus the menu bar on main screen)
95
for (i = 0; i < [screens count]; i++) {
96
NSScreen *screen = [screens objectAtIndex:i];
97
NSRect frame = [screen frame];
98
frame.origin.y = [[NSScreen mainScreen] frame].size.height -
99
frame.size.height - frame.origin.y;
100
if (NSEqualRects([screen frame], [[NSScreen mainScreen] frame])) {
101
frame.origin.y += aquaMenuBarHeight;
102
frame.size.height -= aquaMenuBarHeight;
104
unionRect = NSUnionRect(unionRect, frame);
107
// Use unionRect as the screen size for the X server.
108
*x = unionRect.origin.x;
109
*y = unionRect.origin.y;
110
*width = unionRect.size.width;
111
*height = unionRect.size.height;
112
*rowBytes = (*width) * (*bpp) / 8;
114
// Tell PseudoramiX about the real screens.
115
// InitOutput() will move the big screen to (0,0),
116
// so compensate for that here.
117
for (i = 0; i < [screens count]; i++) {
118
NSScreen *screen = [screens objectAtIndex:i];
119
NSRect frame = [screen frame];
122
// Skip this screen if it's a mirrored copy of an earlier screen.
123
for (j = 0; j < i; j++) {
124
if (NSEqualRects(frame, [[screens objectAtIndex:j] frame])) {
125
ErrorF("PseudoramiX screen %d is a mirror of screen %d.\n",
130
if (j < i) continue; // this screen is a mirrored copy
132
frame.origin.y = [[NSScreen mainScreen] frame].size.height -
133
frame.size.height - frame.origin.y;
135
if (NSEqualRects([screen frame], [[NSScreen mainScreen] frame])) {
136
frame.origin.y += aquaMenuBarHeight;
137
frame.size.height -= aquaMenuBarHeight;
140
ErrorF("PseudoramiX screen %d added: %dx%d @ (%d,%d).\n", i,
141
(int)frame.size.width, (int)frame.size.height,
142
(int)frame.origin.x, (int)frame.origin.y);
144
frame.origin.x -= unionRect.origin.x;
145
frame.origin.y -= unionRect.origin.y;
147
ErrorF("PseudoramiX screen %d placed at X11 coordinate (%d,%d).\n",
148
i, (int)frame.origin.x, (int)frame.origin.y);
150
PseudoramiXAddScreen(frame.origin.x, frame.origin.y,
151
frame.size.width, frame.size.height);
159
* Create a new on-screen window.
160
* Rootless windows must not autodisplay! Autodisplay can cause a deadlock.
161
* Event thread - autodisplay: locks view hierarchy, then window
162
* X Server thread - window resize: locks window, then view hierarchy
163
* Deadlock occurs if each thread gets one lock and waits for the other.
165
void *AquaNewWindow(void *upperw, int x, int y, int w, int h, int isRoot)
167
AquaWindowRec *winRec = (AquaWindowRec *)malloc(sizeof(AquaWindowRec));
168
NSRect frame = NSMakeRect(x, NSHeight([[NSScreen mainScreen] frame]) -
173
// Create an NSWindow for the new X11 window
174
theWindow = [[NSWindow alloc] initWithContentRect:frame
175
styleMask:NSBorderlessWindowMask
176
backing:NSBackingStoreBuffered
178
if (!theWindow) return NULL;
180
[theWindow setBackgroundColor:[NSColor clearColor]]; // erase transparent
181
[theWindow setAlphaValue:1.0]; // draw opaque
182
[theWindow setOpaque:YES]; // changed when window is shaped
184
[theWindow useOptimizedDrawing:YES]; // Has no overlapping sub-views
185
[theWindow setAutodisplay:NO]; // See comment above
186
[theWindow disableFlushWindow]; // We do all the flushing manually
187
[theWindow setHasShadow:!isRoot]; // All windows have shadows except root
188
[theWindow setReleasedWhenClosed:YES]; // Default, but we want to be sure
190
theView = [[XView alloc] initWithFrame:frame];
191
[theWindow setContentView:theView];
192
[theWindow setInitialFirstResponder:theView];
195
AquaWindowRec *upperRec = AQUA_WINREC(upperw);
196
int uppernum = [upperRec->window windowNumber];
197
[theWindow orderWindow:NSWindowBelow relativeTo:uppernum];
200
[theWindow orderFront:nil];
205
[theWindow setAcceptsMouseMovedEvents:YES];
207
winRec->window = theWindow;
208
winRec->view = theView;
211
winRec->rootGWorld = NULL;
213
// Fill the window with white to make sure alpha channel is set
215
winRec->port = [theView qdPort];
216
winRec->context = [[NSGraphicsContext currentContext] graphicsPort];
217
// CreateCGContextForPort(winRec->port, &winRec->context);
218
[theView unlockFocus];
220
// Allocate the offscreen graphics world for root window drawing
221
GWorldPtr rootGWorld;
224
SetRect(&globalRect, x, y, x+w, y+h);
225
if (NewGWorld(&rootGWorld, 0, &globalRect, NULL, NULL, 0))
227
winRec->rootGWorld = rootGWorld;
234
void AquaDestroyWindow(void *rw)
236
AquaWindowRec *winRec = AQUA_WINREC(rw);
238
[winRec->window orderOut:nil];
239
[winRec->window close];
240
[winRec->view release];
241
if (winRec->rootGWorld) {
242
DisposeGWorld(winRec->rootGWorld);
248
void AquaMoveWindow(void *rw, int x, int y)
250
AquaWindowRec *winRec = AQUA_WINREC(rw);
251
NSPoint topLeft = NSMakePoint(x, NSHeight([[NSScreen mainScreen] frame]) -
254
[winRec->window setFrameTopLeftPoint:topLeft];
259
* AquaStartResizeWindow
260
* Resize the on screen window.
262
void AquaStartResizeWindow(void *rw, int x, int y, int w, int h)
264
AquaWindowRec *winRec = AQUA_WINREC(rw);
265
NSRect frame = NSMakeRect(x, NSHeight([[NSScreen mainScreen] frame]) -
268
[winRec->window setFrame:frame display:NO];
272
void AquaFinishResizeWindow(void *rw, int x, int y, int w, int h)
274
// refresh everything? fixme yes for testing
275
fakeBoxRec box = {0, 0, w, h};
276
AquaUpdateRects(rw, &box, 1);
282
* Flush rectangular regions from a window's backing buffer
283
* (or PixMap for the root window) to the screen.
285
void AquaUpdateRects(void *rw, fakeBoxRec *fakeRects, int count)
287
AquaWindowRec *winRec = AQUA_WINREC(rw);
288
fakeBoxRec *rects, *end;
289
static RgnHandle rgn = NULL;
290
static RgnHandle box = NULL;
292
if (!rgn) rgn = NewRgn();
293
if (!box) box = NewRgn();
295
if (winRec->rootGWorld) {
296
// FIXME: Draw from the root PixMap to the normally
297
// invisible root window.
299
for (rects = fakeRects, end = fakeRects+count; rects < end; rects++) {
301
qdrect.left = rects->x1;
302
qdrect.top = rects->y1;
303
qdrect.right = rects->x2;
304
qdrect.bottom = rects->y2;
306
RectRgn(box, &qdrect);
307
UnionRgn(rgn, box, rgn);
310
QDFlushPortBuffer(winRec->port, rgn);
320
* Change the window order. Put the window behind upperw or on top if
323
void AquaRestackWindow(void *rw, void *upperw)
325
AquaWindowRec *winRec = AQUA_WINREC(rw);
328
AquaWindowRec *upperRec = AQUA_WINREC(upperw);
329
int uppernum = [upperRec->window windowNumber];
330
[winRec->window orderWindow:NSWindowBelow relativeTo:uppernum];
332
[winRec->window makeKeyAndOrderFront:nil];
339
* Set the shape of a window. The rectangles are the areas that are
340
* not part of the new shape.
342
void AquaReshapeWindow(void *rw, fakeBoxRec *fakeRects, int count)
344
AquaWindowRec *winRec = AQUA_WINREC(rw);
345
NSRect frame = [winRec->view frame];
346
int winHeight = NSHeight(frame);
348
[winRec->view lockFocus];
350
// If window is currently shaped we need to undo the previous shape
351
if (![winRec->window isOpaque]) {
352
[[NSColor whiteColor] set];
353
NSRectFillUsingOperation(frame, NSCompositeDestinationAtop);
357
fakeBoxRec *rects, *end;
359
// Make transparent if window is now shaped.
360
[winRec->window setOpaque:NO];
362
// Clear the areas outside the window shape
363
[[NSColor clearColor] set];
364
for (rects = fakeRects, end = fakeRects+count; rects < end; rects++) {
365
int rectHeight = rects->y2 - rects->y1;
366
NSRectFill( NSMakeRect(rects->x1,
367
winHeight - rects->y1 - rectHeight,
368
rects->x2 - rects->x1, rectHeight) );
370
[[NSGraphicsContext currentContext] flushGraphics];
372
// force update of window shadow
373
[winRec->window setHasShadow:NO];
374
[winRec->window setHasShadow:YES];
377
fakeBoxRec bounds = {0, 0, NSWidth(frame), winHeight};
379
[winRec->window setOpaque:YES];
380
AquaUpdateRects(rw, &bounds, 1);
383
[winRec->view unlockFocus];
388
* When a window's buffer is not being drawn to, the CoreGraphics
389
* window server may compress or move it. Call this routine
390
* to lock down the buffer during direct drawing. It returns
391
* a pointer to the backing buffer and its depth, etc.
393
void AquaStartDrawing(void *rw, char **bits,
394
int *rowBytes, int *depth, int *bpp)
396
AquaWindowRec *winRec = AQUA_WINREC(rw);
399
if (! winRec->rootGWorld) {
400
[winRec->view lockFocus];
401
winRec->port = [winRec->view qdPort];
402
LockPortBits(winRec->port);
403
[winRec->view unlockFocus];
404
pix = GetPortPixMap(winRec->port);
406
pix = GetGWorldPixMap(winRec->rootGWorld);
410
*bits = GetPixBaseAddr(pix);
411
*rowBytes = GetPixRowBytes(pix) & 0x3fff; // fixme is mask needed?
412
*depth = (**pix).cmpCount * (**pix).cmpSize; // fixme use GetPixDepth(pix)?
413
*bpp = (**pix).pixelSize;
419
* When direct access to a window's buffer is no longer needed, this
420
* routine should be called to allow CoreGraphics to compress or
423
void AquaStopDrawing(void *rw)
425
AquaWindowRec *winRec = AQUA_WINREC(rw);
427
if (! winRec->rootGWorld) {
428
UnlockPortBits(winRec->port);
430
PixMapHandle pix = GetGWorldPixMap(winRec->rootGWorld);