began for maemo
[xscreensaver] / xscreensaver / OSX / osxgrabscreen.m
1 /* xscreensaver, Copyright (c) 1992-2006 Jamie Zawinski <jwz@jwz.org>
2  *
3  * Permission to use, copy, modify, distribute, and sell this software and its
4  * documentation for any purpose is hereby granted without fee, provided that
5  * the above copyright notice appear in all copies and that both that
6  * copyright notice and this permission notice appear in supporting
7  * documentation.  No representations are made about the suitability of this
8  * software for any purpose.  It is provided "as is" without express or 
9  * implied warranty.
10  */
11
12 /* This is the OSX implementation of desktop-grabbing and image-loading.
13  */
14
15 #import <stdlib.h>
16 #import <Cocoa/Cocoa.h>
17 #import "jwxyz.h"
18 #import "grabscreen.h"
19 #import "colorbars.h"
20 #import "resources.h"
21 #import "usleep.h"
22
23
24 static void
25 copy_framebuffer_to_ximage (CGDirectDisplayID cgdpy, XImage *xim,
26                             int window_x, int window_y)
27 {
28   unsigned char *data = (unsigned char *) 
29     CGDisplayAddressForPosition (cgdpy, window_x, window_y);
30   int bpp = CGDisplayBitsPerPixel (cgdpy);
31   int spp = CGDisplaySamplesPerPixel (cgdpy);
32   int bps = CGDisplayBitsPerSample (cgdpy);
33   int bpr = CGDisplayBytesPerRow (cgdpy);
34
35   int y;
36   int ximw = xim->width;
37   int ximh = xim->height;
38
39   unsigned long *odata = (unsigned long *) xim->data;
40
41   switch (bpp) {
42   case 32:
43     if (spp != 3) abort();
44     if (bps != 8) abort();
45     int xwpl = xim->bytes_per_line/4;
46     for (y = 0; y < ximh; y++) {
47       // We can do this because the frame buffer and XImage are both ARGB 32.
48       // Both PPC and Intel use ARGB, viewed in word order (not byte-order).
49       memcpy (odata, data, ximw * 4);
50       odata += xwpl;
51       data += bpr;
52     }
53     break;
54
55   case 16:
56     if (spp != 3) abort();
57     if (bps != 5) abort();
58     for (y = 0; y < ximh; y++) {
59       unsigned short *ip = (unsigned short *) data;
60       int x;
61       for (x = 0; x < ximw; x++) {
62         unsigned short p = *ip++;
63         // This should be ok on both PPC and Intel (ARGB, word order)
64         unsigned char r = (p >> 10) & 0x1F;
65         unsigned char g = (p >>  5) & 0x1F;
66         unsigned char b = (p      ) & 0x1F;
67         r = (r << 3) | (r >> 2);
68         g = (g << 3) | (g >> 2);
69         b = (b << 3) | (b >> 2);
70         unsigned long pixel = 0xFF000000 | (r << 16) | (g << 8) | b;
71         // XPutPixel (xim, x, y, pixel);
72         *odata++ = pixel;
73       }
74       data += bpr;
75     }
76     break;
77
78   case 8:
79     {
80       /* Get the current palette of the display. */
81       CGDirectPaletteRef pal = CGPaletteCreateWithDisplay (cgdpy);
82
83       /* Map it to 32bpp pixels */
84       unsigned long map[256];
85       for (y = 0; y < 256; y++) {
86         CGDeviceColor c = CGPaletteGetColorAtIndex (pal, y);
87         unsigned char r = c.red   * 255.0;
88         unsigned char g = c.green * 255.0;
89         unsigned char b = c.blue  * 255.0;
90         unsigned long pixel = 0xFF000000 | (r << 16) | (g << 8) | b;
91         map[y] = pixel;
92       }
93
94       for (y = 0; y < ximh; y++) {
95         unsigned char *ip = data;
96         int x;
97         for (x = 0; x < ximw; x++) {
98           *odata++ = map[*ip++];
99         }
100         data += bpr;
101       }
102       CGPaletteRelease (pal);
103     }
104     break;
105
106   default:
107     abort();
108     break;
109   }
110 }
111
112
113 /* Loads an image into the Drawable, returning once the image is loaded.
114  */
115 void
116 osx_grab_desktop_image (Screen *screen, Window xwindow, Drawable drawable)
117 {
118   Display *dpy = DisplayOfScreen (screen);
119   XWindowAttributes xgwa;
120   NSView *nsview = jwxyz_window_view (xwindow);
121   NSWindow *nswindow = [nsview window];
122   int window_x, window_y;
123   Window unused;
124
125   // figure out where this window is on the screen
126   //
127   XGetWindowAttributes (dpy, xwindow, &xgwa);
128   XTranslateCoordinates (dpy, xwindow, RootWindowOfScreen (screen), 0, 0, 
129                          &window_x, &window_y, &unused);
130   
131   // Use the size of the Drawable, not the Window.
132   {
133     Window r;
134     int x, y;
135     unsigned int w, h, bbw, d;
136     XGetGeometry (dpy, drawable, &r, &x, &y, &w, &h, &bbw, &d);
137     xgwa.width = w;
138     xgwa.height = h;
139   }
140
141   // Create a tmp ximage to hold the screen data.
142   //
143   XImage *xim = XCreateImage (dpy, xgwa.visual, 32, ZPixmap, 0, 0,
144                               xgwa.width, xgwa.height, 8, 0);
145   xim->data = (char *) malloc (xim->height * xim->bytes_per_line);
146
147
148   // Find the address in the frame buffer of the top left of this window.
149   //
150   CGDirectDisplayID cgdpy = 0;
151   {
152     CGPoint p;
153     // #### this isn't quite right for screen 2: it's offset slightly.
154     p.x = window_x;
155     p.y = window_y;
156     CGDisplayCount n;
157     CGGetDisplaysWithPoint (p, 1, &cgdpy, &n);
158     if (!cgdpy) abort();
159   }
160
161   // Paint a transparent "hole" in this window.
162   //
163   BOOL oopaque = [nswindow isOpaque];
164   [nswindow setOpaque:NO];
165
166   [[NSColor clearColor] set];
167   NSRectFill ([nsview frame]);
168   [[nswindow graphicsContext] flushGraphics];
169
170
171   // Without this, we get a dozen black scanlines at the top.
172   // #### But with this, the screen saver loops, because calling this
173   //      seems to implicitly mark the display as non-idle!
174   // CGDisplayCaptureWithOptions (cgdpy, kCGCaptureNoFill);
175
176   // #### So let's try waiting for the vertical blank instead...
177   //      Nope, that doesn't work.
178   //
179   // CGDisplayWaitForBeamPositionOutsideLines (cgdpy, 0,
180   //   window_y + [nswindow frame].size.height);
181
182   // #### Ok, try a busy-wait?
183   //      Nope.
184   //
185
186   // #### Ok, just fuckin' sleep!
187   //
188   usleep (100000);
189
190
191   // Pull the bits out of the frame buffer.
192   //
193   copy_framebuffer_to_ximage (cgdpy, xim, window_x, window_y);
194
195   // CGDisplayRelease (cgdpy);
196
197   // Make the window visible again.
198   //
199   [nswindow setOpaque:oopaque];
200
201   // Splat the XImage onto the target drawable (probably the window)
202   // and free the bits.
203   //
204   GC gc = 0;
205   XPutImage (dpy, drawable, gc, xim, 0, 0, 0, 0, xim->width, xim->height);
206   XDestroyImage (xim);
207 }
208
209
210 /* Loads an image file and splats it onto the drawable.
211    The image is drawn as large as possible while preserving its aspect ratio.
212    If geom_ret is provided, the actual rectangle the rendered image takes
213    up will be returned there.
214  */
215 Bool
216 osx_load_image_file (Screen *screen, Window xwindow, Drawable drawable,
217                      const char *filename, XRectangle *geom_ret)
218 {
219   NSImage *img = [[NSImage alloc] initWithContentsOfFile:
220                                     [NSString stringWithCString:filename
221                                               encoding:NSUTF8StringEncoding]];
222   if (!img)
223     return False;
224
225   jwxyz_draw_NSImage (DisplayOfScreen (screen), drawable, img, geom_ret);
226   [img release];
227   return True;
228 }