rnd-20140515-1-src
[rocksndiamonds.git] / src / libgame / x11.c
1 /***********************************************************
2 * Artsoft Retro-Game Library                               *
3 *----------------------------------------------------------*
4 * (c) 1994-2006 Artsoft Entertainment                      *
5 *               Holger Schemel                             *
6 *               Detmolder Strasse 189                      *
7 *               33604 Bielefeld                            *
8 *               Germany                                    *
9 *               e-mail: info@artsoft.org                   *
10 *----------------------------------------------------------*
11 * x11.c                                                    *
12 ***********************************************************/
13
14 #include "system.h"
15 #include "pcx.h"
16 #include "misc.h"
17 #include "setup.h"
18
19
20 #if defined(TARGET_X11)
21
22 static void X11InitDisplay();
23 static DrawWindow *X11InitWindow();
24
25 static int X11DebugErrorHandler(Display *display, XErrorEvent *event)
26 {
27   int x = 0;
28
29   return 1 / x;         /* !!! crash program to give backtrace in gdb !!! */
30 }
31
32 void X11InitVideoDisplay(void)
33 {
34   /* initialize X11 video */
35   X11InitDisplay();
36
37   /* set default X11 depth */
38   video.default_depth = XDefaultDepth(display, screen);
39 }
40
41 void X11InitVideoBuffer(DrawBuffer **backbuffer, DrawWindow **window)
42 {
43   if (*window != NULL)
44     X11CloseWindow(*window);
45
46   *window = X11InitWindow();
47
48   XMapWindow(display, (*window)->drawable);
49
50   FlushDisplay();
51
52   /* create additional (off-screen) buffer for double-buffering */
53 #if 1
54   ReCreateBitmap(backbuffer, video.width, video.height, video.depth);
55 #else
56   *backbuffer = CreateBitmap(video.width, video.height, video.depth);
57 #endif
58 }
59
60 static void X11InitDisplay()
61 {
62   XVisualInfo vinfo_template, *vinfo;
63   int num_visuals;
64   unsigned int depth;
65
66   /* connect to X server */
67   if (!(display = XOpenDisplay(options.display_name)))
68     Error(ERR_EXIT, "cannot connect to X server %s",
69           XDisplayName(options.display_name));
70
71   if (options.debug_x11_sync)
72   {
73     Error(ERR_WARN, "running in X11 synchronous mode (debug only)");
74
75     XSynchronize(display, True);
76     XSetErrorHandler(X11DebugErrorHandler);
77   }
78
79   screen = DefaultScreen(display);
80   visual = DefaultVisual(display, screen);
81   depth  = DefaultDepth(display, screen);
82   cmap   = DefaultColormap(display, screen);
83
84   /* look for good enough visual */
85   vinfo_template.screen = screen;
86   vinfo_template.class = (depth == 8 ? PseudoColor : TrueColor);
87   vinfo_template.depth = depth;
88   if ((vinfo = XGetVisualInfo(display, VisualScreenMask | VisualClassMask |
89                               VisualDepthMask, &vinfo_template, &num_visuals)))
90   {
91     visual = vinfo->visual;
92     XFree((void *)vinfo);
93   }
94
95   /* got appropriate visual? */
96   if (depth < 8)
97     Error(ERR_EXIT, "X11 display not supported (less than 8 bits per pixel)");
98   else if ((depth ==8 && visual->class != PseudoColor) ||
99            (depth > 8 && visual->class != TrueColor &&
100             visual->class != DirectColor))
101     Error(ERR_EXIT, "X11 display not supported (inappropriate visual)");
102 }
103
104 static DrawWindow *X11InitWindow()
105 {
106   DrawWindow *new_window = CreateBitmapStruct();
107   unsigned int border_width = 4;
108   XGCValues gc_values;
109   unsigned int gc_valuemask;
110   XTextProperty windowName, iconName;
111   Pixmap icon_pixmap, iconmask_pixmap;
112   unsigned int icon_width, icon_height;
113   int icon_hot_x, icon_hot_y;
114   XSizeHints size_hints;
115   XWMHints wm_hints;
116   XClassHint class_hints;
117   char *window_name = program.window_title;
118   char *icon_name = program.window_title;
119   int window_event_mask;
120   Atom proto_atom = None, delete_atom = None;
121   int screen_width, screen_height;
122   int win_xpos, win_ypos;
123   unsigned int pen_fg = WhitePixel(display, screen);
124   unsigned int pen_bg = BlackPixel(display, screen);
125   const int width = video.width, height = video.height;
126   int i;
127
128   screen_width = XDisplayWidth(display, screen);
129   screen_height = XDisplayHeight(display, screen);
130
131   win_xpos = (screen_width - width) / 2;
132   win_ypos = (screen_height - height) / 2;
133
134   new_window->width = width;
135   new_window->height = height;
136
137   new_window->drawable = XCreateSimpleWindow(display,
138                                              RootWindow(display, screen),
139                                              win_xpos, win_ypos,
140                                              width, height, border_width,
141                                              pen_fg, pen_bg);
142
143   proto_atom = XInternAtom(display, "WM_PROTOCOLS", FALSE);
144   delete_atom = XInternAtom(display, "WM_DELETE_WINDOW", FALSE);
145   if ((proto_atom != None) && (delete_atom != None))
146     XChangeProperty(display, new_window->drawable, proto_atom, XA_ATOM, 32,
147                     PropModePrepend, (unsigned char *) &delete_atom, 1);
148
149   if (XReadBitmapFile(display, new_window->drawable,
150                       getCustomImageFilename(program.x11_icon_filename),
151                       &icon_width, &icon_height, &icon_pixmap,
152                       &icon_hot_x, &icon_hot_y) != BitmapSuccess)
153     Error(ERR_EXIT, "cannot read icon bitmap file '%s'",
154           program.x11_icon_filename);
155
156   if (XReadBitmapFile(display, new_window->drawable,
157                       getCustomImageFilename(program.x11_iconmask_filename),
158                       &icon_width, &icon_height, &iconmask_pixmap,
159                       &icon_hot_x, &icon_hot_y) != BitmapSuccess)
160     Error(ERR_EXIT, "cannot read icon bitmap file '%s'",
161           program.x11_iconmask_filename);
162
163   size_hints.width  = size_hints.min_width  = size_hints.max_width  = width;
164   size_hints.height = size_hints.min_height = size_hints.max_height = height;
165   size_hints.flags = PSize | PMinSize | PMaxSize;
166
167   if (win_xpos || win_ypos)
168   {
169     size_hints.x = win_xpos;
170     size_hints.y = win_ypos;
171     size_hints.flags |= PPosition;
172   }
173
174   if (!XStringListToTextProperty(&window_name, 1, &windowName))
175     Error(ERR_EXIT, "structure allocation for windowName failed");
176
177   if (!XStringListToTextProperty(&icon_name, 1, &iconName))
178     Error(ERR_EXIT, "structure allocation for iconName failed");
179
180   wm_hints.initial_state = NormalState;
181   wm_hints.input = True;
182   wm_hints.icon_pixmap = icon_pixmap;
183   wm_hints.icon_mask = iconmask_pixmap;
184   wm_hints.flags = StateHint | IconPixmapHint | IconMaskHint | InputHint;
185
186   class_hints.res_name = program.command_basename;
187   class_hints.res_class = program.program_title;
188
189   XSetWMProperties(display, new_window->drawable, &windowName, &iconName, 
190                    NULL, 0, &size_hints, &wm_hints, 
191                    &class_hints);
192
193   XFree(windowName.value);
194   XFree(iconName.value);
195
196   /* Select event types wanted */
197   window_event_mask =
198     ExposureMask | StructureNotifyMask | FocusChangeMask |
199     ButtonPressMask | ButtonReleaseMask |
200     PointerMotionMask | PointerMotionHintMask |
201     KeyPressMask | KeyReleaseMask;
202
203   XSelectInput(display, new_window->drawable, window_event_mask);
204
205   /* create GC for drawing with window depth and background color (black) */
206   gc_values.graphics_exposures = False;
207   gc_values.foreground = pen_bg;
208   gc_values.background = pen_bg;
209   gc_valuemask = GCGraphicsExposures | GCForeground | GCBackground;
210   new_window->gc =
211     XCreateGC(display, new_window->drawable, gc_valuemask, &gc_values);
212
213   /* create GCs for line drawing (black and white) */
214   for (i = 0; i < 2; i++)
215   {
216     gc_values.graphics_exposures = False;
217     gc_values.foreground = (i ? pen_fg : pen_bg);
218     gc_values.background = pen_bg;
219     gc_values.line_width = 4;
220     gc_values.line_style = LineSolid;
221     gc_values.cap_style = CapRound;
222     gc_values.join_style = JoinRound;
223
224     gc_valuemask = GCGraphicsExposures | GCForeground | GCBackground |
225                    GCLineWidth | GCLineStyle | GCCapStyle | GCJoinStyle;
226     new_window->line_gc[i] =
227       XCreateGC(display, new_window->drawable, gc_valuemask, &gc_values);
228   }
229
230   return new_window;
231 }
232
233 void X11CloseWindow(DrawWindow *window)
234 {
235   if (window->drawable)
236   {
237     XUnmapWindow(display, window->drawable);
238     XDestroyWindow(display, window->drawable);
239   }
240
241   if (window->gc)
242     XFreeGC(display, window->gc);
243
244   free(window);
245 }
246
247 void X11ZoomBitmap(Bitmap *src_bitmap, Bitmap *dst_bitmap)
248 {
249 #if defined(TARGET_ALLEGRO)
250   AllegroZoomBitmap(src_bitmap->drawable, dst_bitmap->drawable,
251                     src_bitmap->width, src_bitmap->height,
252                     dst_bitmap->width, dst_bitmap->height);
253 #else
254   ZoomPixmap(display, src_bitmap->gc,
255              src_bitmap->drawable, dst_bitmap->drawable,
256              src_bitmap->width, src_bitmap->height,
257              dst_bitmap->width, dst_bitmap->height);
258 #endif
259 }
260
261 static void SetImageDimensions(Bitmap *bitmap)
262 {
263 #if defined(TARGET_ALLEGRO)
264   BITMAP *allegro_bitmap = (BITMAP *)(bitmap->drawable);
265
266   bitmap->width  = allegro_bitmap->w;
267   bitmap->height = allegro_bitmap->h;
268 #else
269   Window root;
270   int x, y;
271   unsigned int border_width, depth;
272
273   XGetGeometry(display, bitmap->drawable, &root, &x, &y,
274                &bitmap->width, &bitmap->height, &border_width, &depth);
275 #endif
276 }
277
278 Bitmap *X11LoadImage(char *filename)
279 {
280   Bitmap *new_bitmap = CreateBitmapStruct();
281   char *error = "Read_PCX_to_Pixmap(): %s '%s'";
282   int pcx_err;
283   XGCValues clip_gc_values;
284   unsigned int clip_gc_valuemask;
285
286   pcx_err = Read_PCX_to_Pixmap(display, window->drawable, window->gc, filename,
287                                &new_bitmap->drawable, &new_bitmap->clip_mask);
288   switch(pcx_err)
289   {
290     case PCX_Success:
291       break;
292     case PCX_OpenFailed:
293       SetError(error, "cannot open PCX file", filename);
294       return NULL;
295     case PCX_ReadFailed:
296       SetError(error, "cannot read PCX file", filename);
297       return NULL;
298     case PCX_FileInvalid:
299       SetError(error, "invalid PCX file", filename);
300       return NULL;
301     case PCX_NoMemory:
302       SetError(error, "not enough memory for PCX file", filename);
303       return NULL;
304     case PCX_ColorFailed:
305       SetError(error, "cannot get colors for PCX file", filename);
306       return NULL;
307     case PCX_OtherError:
308       /* this should already have called SetError() */
309       return NULL;
310     default:
311       SetError(error, "unknown error reading PCX file", filename);
312       return NULL;
313   }
314
315   if (!new_bitmap->drawable)
316   {
317     SetError("X11LoadImage(): cannot get graphics for '%s'", filename);
318     return NULL;
319   }
320
321   if (!new_bitmap->clip_mask)
322   {
323     SetError("X11LoadImage(): cannot get clipmask for '%s'", filename);
324     return NULL;
325   }
326
327   clip_gc_values.graphics_exposures = False;
328   clip_gc_values.clip_mask = new_bitmap->clip_mask;
329   clip_gc_valuemask = GCGraphicsExposures | GCClipMask;
330   new_bitmap->stored_clip_gc = XCreateGC(display, window->drawable,
331                                          clip_gc_valuemask, &clip_gc_values);
332
333   /* set GraphicContext inheritated from Window */
334   new_bitmap->gc = window->gc;
335
336   /* set image width and height */
337   SetImageDimensions(new_bitmap);
338
339   return new_bitmap;
340 }
341
342 void X11CreateBitmapContent(Bitmap *new_bitmap,
343                             int width, int height, int depth)
344 {
345   Pixmap pixmap;
346
347   if ((pixmap = XCreatePixmap(display, window->drawable, width, height, depth))
348       == None)
349     Error(ERR_EXIT, "cannot create pixmap");
350
351   new_bitmap->drawable = pixmap;
352
353   if (window == NULL)
354     Error(ERR_EXIT, "Window GC needed for Bitmap -- create Window first");
355
356   new_bitmap->gc = window->gc;
357
358   new_bitmap->line_gc[0] = window->line_gc[0];
359   new_bitmap->line_gc[1] = window->line_gc[1];
360 }
361
362 void X11FreeBitmapPointers(Bitmap *bitmap)
363 {
364   /* The X11 version seems to have a memory leak here -- although
365      "XFreePixmap()" is called, the corresponding memory seems not
366      to be freed (according to "ps"). The SDL version apparently
367      does not have this problem. */
368
369   if (bitmap->drawable)
370     XFreePixmap(display, bitmap->drawable);
371   if (bitmap->clip_mask)
372     XFreePixmap(display, bitmap->clip_mask);
373   if (bitmap->stored_clip_gc)
374     XFreeGC(display, bitmap->stored_clip_gc);
375   /* the other GCs are only pointers to GCs used elsewhere */
376   bitmap->drawable = None;
377   bitmap->clip_mask = None;
378   bitmap->stored_clip_gc = None;
379 }
380
381 void X11CopyArea(Bitmap *src_bitmap, Bitmap *dst_bitmap,
382                  int src_x, int src_y, int width, int height,
383                  int dst_x, int dst_y, int mask_mode)
384 {
385   XCopyArea(display, src_bitmap->drawable, dst_bitmap->drawable,
386             (mask_mode == BLIT_MASKED ? src_bitmap->clip_gc : dst_bitmap->gc),
387             src_x, src_y, width, height, dst_x, dst_y);
388 }
389
390 void X11FillRectangle(Bitmap *bitmap, int x, int y,
391                       int width, int height, Pixel color)
392 {
393   XSetForeground(display, bitmap->gc, color);
394   XFillRectangle(display, bitmap->drawable, bitmap->gc, x, y, width, height);
395 }
396
397 void X11FadeRectangle(Bitmap *bitmap_cross, int x, int y, int width, int height,
398                       int fade_mode, int fade_delay, int post_delay,
399                       void (*draw_border_function)(void))
400 {
401   /* fading currently not supported -- simply copy backbuffer to screen */
402
403   if (fade_mode == FADE_MODE_FADE_OUT)
404     X11FillRectangle(backbuffer, x, y, width, height, BLACK_PIXEL);
405
406   if (draw_border_function != NULL)
407     draw_border_function();
408
409   X11CopyArea(backbuffer, window, x, y, width, height, x, y, BLIT_OPAQUE);
410
411   /* as we currently cannot use the fade delay, also do not use post delay */
412 }
413
414 void X11DrawSimpleLine(Bitmap *bitmap, int from_x, int from_y,
415                        int to_x, int to_y, Pixel color)
416 {
417   XSetForeground(display, bitmap->gc, color);
418   XDrawLine(display, bitmap->drawable, bitmap->gc, from_x, from_y, to_x, to_y);
419 }
420
421 Pixel X11GetPixel(Bitmap *bitmap, int x, int y)
422 {
423   XImage *pixel_image;
424   Pixel pixel_value;
425
426   pixel_image = XGetImage(display, bitmap->drawable, x, y, 1, 1,
427                           AllPlanes, ZPixmap);
428   pixel_value = XGetPixel(pixel_image, 0, 0);
429
430   X11DestroyImage(pixel_image);
431
432   return pixel_value;
433 }
434
435 #if defined(TARGET_X11_NATIVE)
436 Pixel X11GetPixelFromRGB(unsigned int color_r, unsigned int color_g,
437                          unsigned int color_b)
438 {
439   XColor xcolor;
440   Pixel pixel;
441
442   xcolor.flags = DoRed | DoGreen | DoBlue;
443   xcolor.red = (color_r << 8);
444   xcolor.green = (color_g << 8);
445   xcolor.blue = (color_b << 8);
446
447   XAllocColor(display, cmap, &xcolor);
448   pixel = xcolor.pixel;
449
450   return pixel;
451 }
452 #endif  /* TARGET_X11_NATIVE */
453
454 void X11DestroyImage(XImage *ximage)
455 {
456 #if defined(TARGET_X11_NATIVE)
457   /* this seems to be needed for OS/2, but does not hurt on other platforms */
458   if (ximage->data != NULL)
459   {
460     free(ximage->data);
461     ximage->data = NULL;
462   }
463 #endif  /* TARGET_X11_NATIVE */
464
465   XDestroyImage(ximage);
466 }
467
468
469 /* ------------------------------------------------------------------------- */
470 /* mouse pointer functions                                                   */
471 /* ------------------------------------------------------------------------- */
472
473 #if defined(TARGET_X11_NATIVE)
474
475 static Cursor create_cursor(struct MouseCursorInfo *cursor_info)
476 {
477   Pixmap pixmap_data, pixmap_mask;
478   XColor color_fg, color_bg;
479   Cursor cursor;
480
481   /* shape and mask are single plane pixmaps */
482   pixmap_data =
483     XCreatePixmapFromBitmapData(display, window->drawable, cursor_info->data,
484                                 cursor_info->width, cursor_info->height,
485                                 1, 0, 1);
486   pixmap_mask =
487     XCreatePixmapFromBitmapData(display, window->drawable, cursor_info->mask,
488                                 cursor_info->width, cursor_info->height,
489                                 1, 0, 1);
490
491   XParseColor(display, cmap, "black", &color_fg);
492   XParseColor(display, cmap, "white", &color_bg);
493
494   cursor = XCreatePixmapCursor(display, pixmap_data, pixmap_mask,
495                                &color_fg, &color_bg,
496                                cursor_info->hot_x, cursor_info->hot_y);
497
498   return cursor;
499 }
500
501 void X11SetMouseCursor(struct MouseCursorInfo *cursor_info)
502 {
503   static struct MouseCursorInfo *last_cursor_info = NULL;
504   static Cursor cursor_default = None;
505   static Cursor cursor_current = None;
506
507   if (cursor_info != NULL && cursor_info != last_cursor_info)
508   {
509     cursor_current = create_cursor(cursor_info);
510     last_cursor_info = cursor_info;
511   }
512
513   XDefineCursor(display, window->drawable,
514                 cursor_info ? cursor_current : cursor_default);
515 }
516 #endif  /* TARGET_X11_NATIVE */
517
518 #endif /* TARGET_X11 */