1/*
2 * Copyright (C) 2010 LunarG Inc.
3 *
4 * Permission is hereby granted, free of charge, to any person obtaining a
5 * copy of this software and associated documentation files (the "Software"),
6 * to deal in the Software without restriction, including without limitation
7 * the rights to use, copy, modify, merge, publish, distribute, sublicense,
8 * and/or sell copies of the Software, and to permit persons to whom the
9 * Software is furnished to do so, subject to the following conditions:
10 *
11 * The above copyright notice and this permission notice shall be included
12 * in all copies or substantial portions of the Software.
13 *
14 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
15 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
16 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
17 * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
18 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
19 * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
20 * DEALINGS IN THE SOFTWARE.
21 *
22 * Authors:
23 *    Chia-I Wu <olv@lunarg.com>
24 */
25
26#include <X11/Xlib.h>
27#include <X11/Xutil.h>
28#include <X11/keysym.h>
29
30#include "eglutint.h"
31
32void
33_eglutNativeInitDisplay(void)
34{
35   _eglut->native_dpy = XOpenDisplay(_eglut->display_name);
36   if (!_eglut->native_dpy)
37      _eglutFatal("failed to initialize native display");
38
39   _eglut->surface_type = EGL_WINDOW_BIT;
40}
41
42void
43_eglutNativeFiniDisplay(void)
44{
45   XCloseDisplay(_eglut->native_dpy);
46}
47
48void
49_eglutNativeInitWindow(struct eglut_window *win, const char *title,
50                       int x, int y, int w, int h)
51{
52   XVisualInfo *visInfo, visTemplate;
53   int num_visuals;
54   Window root, xwin;
55   XSetWindowAttributes attr;
56   unsigned long mask;
57   EGLint vid;
58
59   if (!eglGetConfigAttrib(_eglut->dpy,
60            win->config, EGL_NATIVE_VISUAL_ID, &vid))
61      _eglutFatal("failed to get visual id");
62
63   /* The X window visual must match the EGL config */
64   visTemplate.visualid = vid;
65   visInfo = XGetVisualInfo(_eglut->native_dpy,
66         VisualIDMask, &visTemplate, &num_visuals);
67   if (!visInfo)
68      _eglutFatal("failed to get an visual of id 0x%x", vid);
69
70   root = RootWindow(_eglut->native_dpy, DefaultScreen(_eglut->native_dpy));
71
72   /* window attributes */
73   attr.background_pixel = 0;
74   attr.border_pixel = 0;
75   attr.colormap = XCreateColormap(_eglut->native_dpy,
76         root, visInfo->visual, AllocNone);
77   attr.event_mask = StructureNotifyMask | ExposureMask | KeyPressMask;
78   mask = CWBackPixel | CWBorderPixel | CWColormap | CWEventMask;
79
80   xwin = XCreateWindow(_eglut->native_dpy, root, x, y, w, h,
81         0, visInfo->depth, InputOutput, visInfo->visual, mask, &attr);
82   if (!xwin)
83      _eglutFatal("failed to create a window");
84
85   XFree(visInfo);
86
87   /* set hints and properties */
88   {
89      XSizeHints sizehints;
90      sizehints.x = x;
91      sizehints.y = y;
92      sizehints.width  = w;
93      sizehints.height = h;
94      sizehints.flags = USSize | USPosition;
95      XSetNormalHints(_eglut->native_dpy, xwin, &sizehints);
96      XSetStandardProperties(_eglut->native_dpy, xwin,
97            title, title, None, (char **) NULL, 0, &sizehints);
98   }
99
100   XMapWindow(_eglut->native_dpy, xwin);
101
102   win->native.u.window = xwin;
103   win->native.width = w;
104   win->native.height = h;
105}
106
107void
108_eglutNativeFiniWindow(struct eglut_window *win)
109{
110   XDestroyWindow(_eglut->native_dpy, win->native.u.window);
111}
112
113static int
114lookup_keysym(KeySym sym)
115{
116   int special;
117
118   switch (sym) {
119   case XK_F1:
120      special = EGLUT_KEY_F1;
121      break;
122   case XK_F2:
123      special = EGLUT_KEY_F2;
124      break;
125   case XK_F3:
126      special = EGLUT_KEY_F3;
127      break;
128   case XK_F4:
129      special = EGLUT_KEY_F4;
130      break;
131   case XK_F5:
132      special = EGLUT_KEY_F5;
133      break;
134   case XK_F6:
135      special = EGLUT_KEY_F6;
136      break;
137   case XK_F7:
138      special = EGLUT_KEY_F7;
139      break;
140   case XK_F8:
141      special = EGLUT_KEY_F8;
142      break;
143   case XK_F9:
144      special = EGLUT_KEY_F9;
145      break;
146   case XK_F10:
147      special = EGLUT_KEY_F10;
148      break;
149   case XK_F11:
150      special = EGLUT_KEY_F11;
151      break;
152   case XK_F12:
153      special = EGLUT_KEY_F12;
154      break;
155   case XK_KP_Left:
156   case XK_Left:
157      special = EGLUT_KEY_LEFT;
158      break;
159   case XK_KP_Up:
160   case XK_Up:
161      special = EGLUT_KEY_UP;
162      break;
163   case XK_KP_Right:
164   case XK_Right:
165      special = EGLUT_KEY_RIGHT;
166      break;
167   case XK_KP_Down:
168   case XK_Down:
169      special = EGLUT_KEY_DOWN;
170      break;
171   default:
172      special = -1;
173      break;
174   }
175
176   return special;
177}
178
179static void
180next_event(struct eglut_window *win)
181{
182   int redraw = 0;
183   XEvent event;
184
185   if (!XPending(_eglut->native_dpy)) {
186      /* there is an idle callback */
187      if (_eglut->idle_cb) {
188         _eglut->idle_cb();
189         return;
190      }
191
192      /* the app requests re-display */
193      if (_eglut->redisplay)
194         return;
195   }
196
197   /* block for next event */
198   XNextEvent(_eglut->native_dpy, &event);
199
200   switch (event.type) {
201   case Expose:
202      redraw = 1;
203      break;
204   case ConfigureNotify:
205      win->native.width = event.xconfigure.width;
206      win->native.height = event.xconfigure.height;
207      if (win->reshape_cb)
208         win->reshape_cb(win->native.width, win->native.height);
209      break;
210   case KeyPress:
211      {
212         char buffer[1];
213         KeySym sym;
214         int r;
215
216         r = XLookupString(&event.xkey,
217               buffer, sizeof(buffer), &sym, NULL);
218         if (r && win->keyboard_cb) {
219            win->keyboard_cb(buffer[0]);
220         }
221         else if (!r && win->special_cb) {
222            r = lookup_keysym(sym);
223            if (r >= 0)
224               win->special_cb(r);
225         }
226      }
227      redraw = 1;
228      break;
229   default:
230      ; /*no-op*/
231   }
232
233   _eglut->redisplay = redraw;
234}
235
236void
237_eglutNativeEventLoop(void)
238{
239   while (1) {
240      struct eglut_window *win = _eglut->current;
241
242      next_event(win);
243
244      if (_eglut->redisplay) {
245         _eglut->redisplay = 0;
246
247         if (win->display_cb)
248            win->display_cb();
249         eglSwapBuffers(_eglut->dpy, win->surface);
250      }
251   }
252}
253