nuclear@7: /* SimplyGL window system module for X11/GLX */ nuclear@9: /* link-with: -lX11 */ nuclear@7: nuclear@7: #include "config.h" nuclear@7: nuclear@7: #ifdef USE_WSYS_MODULE_X11 nuclear@7: nuclear@4: #include nuclear@7: #include nuclear@3: #include nuclear@3: #include nuclear@4: #include "sgl.h" nuclear@3: #include "wsys.h" nuclear@4: #include "log.h" nuclear@3: nuclear@5: nuclear@3: struct window { nuclear@3: Window win; nuclear@3: GLXContext ctx; nuclear@5: int width, height; nuclear@5: int mapped; nuclear@4: long evmask; nuclear@5: int redisp_pending; nuclear@3: struct window *next; nuclear@3: }; nuclear@3: nuclear@3: static int init(void); nuclear@3: static void shutdown(void); nuclear@4: nuclear@4: /* video mode switching */ nuclear@4: static int set_vidmode(int xsz, int ysz); nuclear@4: static int get_vidmode(int *xsz, int *ysz); nuclear@4: nuclear@4: /* create/destroy windows */ nuclear@3: static int create_window(int xsz, int ysz, unsigned int flags); nuclear@4: static void fill_attr(int *attr, unsigned int flags); nuclear@4: static void print_visual_info(XVisualInfo *vis); nuclear@3: static void close_window(int id); nuclear@3: nuclear@4: /* window management */ nuclear@4: static int set_active(int id); nuclear@5: static struct window *find_window(int id); nuclear@5: static int activate_window(struct window *win); nuclear@4: static int set_title(const char *str); nuclear@4: static void redisplay(void); nuclear@4: static void swap_buffers(void); nuclear@4: nuclear@7: static int get_modifiers(void); nuclear@7: nuclear@4: /* event handling and friends */ nuclear@4: static void set_bits(long *mask, long bits); nuclear@4: static void clear_bits(long *mask, long bits); nuclear@4: static void set_event(int idx, int enable); nuclear@4: static int process_events(void); nuclear@5: static int handle_event(XEvent *xev); nuclear@7: static void process_key(KeySym sym, int state); nuclear@5: static int translate_keysym(KeySym sym); nuclear@4: nuclear@3: static struct wsys_module ws = { nuclear@3: "x11-glx", 0, nuclear@3: init, nuclear@3: shutdown, nuclear@3: set_vidmode, nuclear@3: get_vidmode, nuclear@3: create_window, nuclear@3: close_window, nuclear@4: set_active, nuclear@4: set_title, nuclear@4: redisplay, nuclear@4: swap_buffers, nuclear@7: get_modifiers, nuclear@4: set_event, nuclear@4: process_events, nuclear@3: 0 nuclear@3: }; nuclear@3: nuclear@3: static Display *dpy; nuclear@4: static Window root; nuclear@3: static int scr; nuclear@4: static Atom xa_wm_prot, xa_wm_del_win; nuclear@3: static struct window *winlist; nuclear@5: static struct window *active_win, *prev_active; nuclear@7: static int modkeys; nuclear@3: nuclear@4: /* this is the only exported function, everything else should be static */ nuclear@3: void sgl_register_x11(void) nuclear@3: { nuclear@3: sgl_register_module(&ws); nuclear@3: } nuclear@3: nuclear@3: static int init(void) nuclear@3: { nuclear@5: if(dpy) { nuclear@5: sgl_log("warning: double init\n"); nuclear@5: return 0; nuclear@5: } nuclear@5: nuclear@3: winlist = 0; nuclear@5: active_win = prev_active = 0; nuclear@3: nuclear@3: if(!(dpy = XOpenDisplay(0))) { nuclear@4: sgl_log("failed to open X display: %s\n", XDisplayName(0)); nuclear@3: return -1; nuclear@3: } nuclear@3: scr = DefaultScreen(dpy); nuclear@4: root = RootWindow(dpy, scr); nuclear@4: nuclear@4: xa_wm_prot = XInternAtom(dpy, "WM_PROTOCOLS", False); nuclear@4: xa_wm_del_win = XInternAtom(dpy, "WM_DELETE_WINDOW", False); nuclear@3: nuclear@3: return 0; nuclear@3: } nuclear@3: nuclear@3: static void shutdown(void) nuclear@3: { nuclear@5: if(!dpy) { nuclear@5: return; nuclear@5: } nuclear@5: nuclear@3: while(winlist) { nuclear@3: struct window *win = winlist; nuclear@3: winlist = winlist->next; nuclear@3: nuclear@3: glXDestroyContext(dpy, win->ctx); nuclear@4: XDestroyWindow(dpy, win->win); nuclear@3: free(win); nuclear@3: } nuclear@3: XCloseDisplay(dpy); nuclear@3: dpy = 0; nuclear@3: } nuclear@3: nuclear@4: static int set_vidmode(int xsz, int ysz) nuclear@3: { nuclear@3: /* TODO */ nuclear@4: return 0; nuclear@3: } nuclear@3: nuclear@4: static int get_vidmode(int *xsz, int *ysz) nuclear@3: { nuclear@3: /* TODO */ nuclear@4: return 0; nuclear@3: } nuclear@3: nuclear@3: static int create_window(int xsz, int ysz, unsigned int flags) nuclear@3: { nuclear@4: int attr[32]; nuclear@4: Window win; nuclear@4: GLXContext ctx; nuclear@4: XVisualInfo *vis; nuclear@4: XClassHint chint; nuclear@4: XSetWindowAttributes xattr; nuclear@4: unsigned int attr_valid; nuclear@4: long evmask; nuclear@4: struct window *wnode; nuclear@6: void (*func)(); nuclear@4: nuclear@4: if(!(wnode = malloc(sizeof *wnode))) { nuclear@4: return -1; nuclear@4: } nuclear@4: nuclear@4: fill_attr(attr, flags); nuclear@4: nuclear@4: if(!(vis = glXChooseVisual(dpy, scr, attr))) { nuclear@4: sgl_log("failed to find a suitable visual\n"); nuclear@4: free(wnode); nuclear@4: return -1; nuclear@4: } nuclear@4: print_visual_info(vis); nuclear@4: nuclear@4: if(!(ctx = glXCreateContext(dpy, vis, 0, True))) { nuclear@4: sgl_log("failed to create OpenGL context\n"); nuclear@4: XFree(vis); nuclear@4: free(wnode); nuclear@4: return -1; nuclear@4: } nuclear@4: nuclear@4: xattr.background_pixel = xattr.border_pixel = BlackPixel(dpy, scr); nuclear@4: xattr.colormap = XCreateColormap(dpy, root, vis->visual, AllocNone); nuclear@4: attr_valid = CWColormap | CWBackPixel | CWBorderPixel; nuclear@4: nuclear@4: if(!(win = XCreateWindow(dpy, root, 0, 0, xsz, ysz, 0, vis->depth, InputOutput, nuclear@4: vis->visual, attr_valid, &xattr))) { nuclear@4: sgl_log("failed to create window\n"); nuclear@4: glXDestroyContext(dpy, ctx); nuclear@4: XFree(vis); nuclear@4: free(wnode); nuclear@4: return -1; nuclear@4: } nuclear@4: XFree(vis); nuclear@4: nuclear@4: evmask = StructureNotifyMask | VisibilityChangeMask; nuclear@4: XSelectInput(dpy, win, evmask); nuclear@4: nuclear@4: XSetWMProtocols(dpy, win, &xa_wm_del_win, 1); nuclear@4: nuclear@4: set_title("OpenGL/X11"); nuclear@4: nuclear@4: chint.res_name = chint.res_class = "simplygl"; nuclear@4: XSetClassHint(dpy, win, &chint); nuclear@4: nuclear@4: XMapWindow(dpy, win); nuclear@4: nuclear@4: wnode->win = win; nuclear@4: wnode->ctx = ctx; nuclear@5: wnode->width = xsz; nuclear@5: wnode->height = ysz; nuclear@5: wnode->mapped = 0; nuclear@4: wnode->evmask = evmask; nuclear@5: wnode->redisp_pending = 1; nuclear@4: wnode->next = winlist; nuclear@5: winlist = wnode; nuclear@4: nuclear@4: if(!active_win) { nuclear@4: set_active(win); nuclear@6: } else { nuclear@6: activate_window(wnode); nuclear@4: } nuclear@6: nuclear@6: if((func = sgl_get_callback(SGL_CREATE))) { nuclear@6: func(win); nuclear@6: } nuclear@6: if((func = sgl_get_callback(SGL_RESHAPE))) { nuclear@6: func(xsz, ysz); nuclear@6: } nuclear@6: activate_window(prev_active); nuclear@4: return win; nuclear@4: } nuclear@4: nuclear@4: static void fill_attr(int *attr, unsigned int flags) nuclear@4: { nuclear@4: int i = 0; nuclear@4: nuclear@4: attr[i++] = GLX_RGBA; nuclear@4: attr[i++] = GLX_RED_SIZE; attr[i++] = 1; nuclear@4: attr[i++] = GLX_GREEN_SIZE; attr[i++] = 1; nuclear@4: attr[i++] = GLX_BLUE_SIZE; attr[i++] = 1; nuclear@4: nuclear@4: if(flags & SGL_DOUBLE) { nuclear@4: attr[i++] = GLX_DOUBLEBUFFER; nuclear@4: } nuclear@4: if(flags & SGL_DEPTH) { nuclear@4: attr[i++] = GLX_DEPTH_SIZE; nuclear@4: attr[i++] = 1; nuclear@4: } nuclear@4: if(flags & SGL_STENCIL) { nuclear@4: attr[i++] = GLX_STENCIL_SIZE; nuclear@4: attr[i++] = 1; nuclear@4: } nuclear@4: if(flags & SGL_STEREO) { nuclear@4: attr[i++] = GLX_STEREO; nuclear@4: } nuclear@4: #if defined(GLX_VERSION_1_4) || defined(GLX_ARB_multisample) nuclear@4: if(flags & SGL_MULTISAMPLE) { nuclear@4: attr[i++] = GLX_SAMPLE_BUFFERS_ARB; nuclear@4: attr[i++] = 1; nuclear@4: attr[i++] = GLX_SAMPLES_ARB; nuclear@4: attr[i++] = 1; nuclear@4: } nuclear@4: #endif /* defined GLX_VERSION_1_4 || GLX_ARB_multisample */ nuclear@4: attr[i] = None; nuclear@4: } nuclear@4: nuclear@4: static void print_visual_info(XVisualInfo *vis) nuclear@4: { nuclear@4: int rbits, gbits, bbits, zbits, sbits, stereo, aa, samples; nuclear@4: nuclear@4: glXGetConfig(dpy, vis, GLX_RED_SIZE, &rbits); nuclear@4: glXGetConfig(dpy, vis, GLX_GREEN_SIZE, &gbits); nuclear@4: glXGetConfig(dpy, vis, GLX_BLUE_SIZE, &bbits); nuclear@4: glXGetConfig(dpy, vis, GLX_DEPTH_SIZE, &zbits); nuclear@4: glXGetConfig(dpy, vis, GLX_STENCIL_SIZE, &sbits); nuclear@4: glXGetConfig(dpy, vis, GLX_STEREO, &stereo); nuclear@4: #if defined(GLX_VERSION_1_4) || defined(GLX_ARB_multisample) nuclear@4: glXGetConfig(dpy, vis, GLX_SAMPLE_BUFFERS_ARB, &aa); nuclear@4: if(aa) { nuclear@4: glXGetConfig(dpy, vis, GLX_SAMPLES_ARB, &samples); nuclear@4: } else { nuclear@4: samples = 1; nuclear@4: } nuclear@4: #endif /* defined GLX_VERSION_1_4 || GLX_ARB_multisample */ nuclear@4: nuclear@4: sgl_log("got visual: %d%d%d d:%d s:%d", rbits, gbits, bbits, zbits, sbits); nuclear@4: sgl_log(" %s", stereo ? "stereo" : "mono"); nuclear@4: sgl_log(" samples/pixel: %d\n", samples); nuclear@3: } nuclear@3: nuclear@3: static void close_window(int id) nuclear@3: { nuclear@3: struct window dummy, *win, *prev; nuclear@6: sgl_close_callback_t close_func; nuclear@6: nuclear@3: dummy.next = winlist; nuclear@3: nuclear@3: prev = &dummy; nuclear@3: win = prev->next; nuclear@3: nuclear@3: while(win) { nuclear@3: if(win->win == id) { nuclear@6: if(!(close_func = sgl_get_callback(SGL_CLOSE))) { nuclear@6: close_func(id); nuclear@6: } nuclear@3: glXDestroyContext(dpy, win->ctx); nuclear@4: XDestroyWindow(dpy, win->win); nuclear@3: prev->next = win->next; nuclear@3: free(win); nuclear@3: return; nuclear@3: } nuclear@3: win = win->next; nuclear@3: } nuclear@3: } nuclear@4: nuclear@4: static int set_active(int id) nuclear@4: { nuclear@5: struct window *win = find_window(id); nuclear@5: if(!win) { nuclear@5: sgl_log("no such window: %d\n", id); nuclear@5: return -1; nuclear@5: } nuclear@5: /* only the user calls this, so don't revert this selection */ nuclear@5: prev_active = win; nuclear@5: return activate_window(win); nuclear@5: } nuclear@5: nuclear@5: static struct window *find_window(int id) nuclear@5: { nuclear@4: struct window *win = winlist; nuclear@4: nuclear@4: while(win) { nuclear@4: if(win->win == id) { nuclear@5: return win; nuclear@4: } nuclear@4: win = win->next; nuclear@4: } nuclear@5: return 0; nuclear@5: } nuclear@4: nuclear@5: static int activate_window(struct window *win) nuclear@5: { nuclear@5: if(glXMakeCurrent(dpy, win->win, win->ctx) == False) { nuclear@5: sgl_log("failed to activate window %d\n", (int)win->win); nuclear@5: return -1; nuclear@5: } nuclear@5: active_win = win; nuclear@5: return 0; nuclear@4: } nuclear@4: nuclear@4: static int set_title(const char *str) nuclear@4: { nuclear@4: XTextProperty wm_name; nuclear@4: nuclear@4: if(!str || !active_win) { nuclear@4: return -1; nuclear@4: } nuclear@4: XStringListToTextProperty((char**)&str, 1, &wm_name); nuclear@4: XSetWMName(dpy, active_win->win, &wm_name); nuclear@4: XSetWMIconName(dpy, active_win->win, &wm_name); nuclear@4: XFree(wm_name.value); nuclear@4: return 0; nuclear@4: } nuclear@4: nuclear@4: static void redisplay(void) nuclear@4: { nuclear@4: active_win->redisp_pending = 1; nuclear@4: } nuclear@4: nuclear@4: static void swap_buffers(void) nuclear@4: { nuclear@5: glXSwapBuffers(dpy, active_win->win); nuclear@4: } nuclear@4: nuclear@7: static int get_modifiers(void) nuclear@7: { nuclear@7: return modkeys; nuclear@7: } nuclear@7: nuclear@4: static void set_bits(long *mask, long bits) nuclear@4: { nuclear@4: *mask |= bits; nuclear@4: } nuclear@4: nuclear@4: static void clear_bits(long *mask, long bits) nuclear@4: { nuclear@4: *mask &= ~bits; nuclear@4: } nuclear@4: nuclear@4: static void set_event(int idx, int enable) nuclear@4: { nuclear@4: void (*op)(long*, long); nuclear@4: op = enable ? set_bits : clear_bits; nuclear@4: nuclear@4: switch(idx) { nuclear@4: case SGL_DISPLAY: nuclear@4: op(&active_win->evmask, ExposureMask); nuclear@4: break; nuclear@4: nuclear@4: case SGL_KEYBOARD: nuclear@4: op(&active_win->evmask, KeyPressMask | KeyReleaseMask); nuclear@4: break; nuclear@4: nuclear@4: case SGL_MOUSE: nuclear@4: op(&active_win->evmask, ButtonPressMask | ButtonReleaseMask); nuclear@4: break; nuclear@4: nuclear@4: case SGL_MOTION: nuclear@4: op(&active_win->evmask, ButtonMotionMask); nuclear@4: break; nuclear@4: nuclear@4: case SGL_PASSIVE: nuclear@4: op(&active_win->evmask, PointerMotionMask); nuclear@4: break; nuclear@4: nuclear@4: default: nuclear@4: return; nuclear@4: } nuclear@4: nuclear@4: XSelectInput(dpy, active_win->win, active_win->evmask); nuclear@4: } nuclear@4: nuclear@4: static int process_events(void) nuclear@4: { nuclear@4: XEvent xev; nuclear@4: void (*func)(); nuclear@5: struct window *win; nuclear@4: nuclear@5: prev_active = active_win; nuclear@5: nuclear@5: win = winlist; nuclear@5: while(win) { nuclear@5: if(win->redisp_pending && (func = sgl_get_callback(SGL_DISPLAY))) { nuclear@5: activate_window(win); nuclear@5: func(); nuclear@5: win->redisp_pending = 0; nuclear@5: } nuclear@5: win = win->next; nuclear@5: } nuclear@5: nuclear@5: func = sgl_get_callback(SGL_IDLE); nuclear@5: if(!func) { nuclear@5: XNextEvent(dpy, &xev); nuclear@5: XPutBackEvent(dpy, &xev); nuclear@5: } nuclear@5: nuclear@5: /* process all pending events... */ nuclear@4: while(XPending(dpy)) { nuclear@4: XNextEvent(dpy, &xev); nuclear@5: if(handle_event(&xev) == -1) { nuclear@5: return -1; nuclear@5: } nuclear@4: nuclear@5: if(!dpy) { nuclear@5: return -1; nuclear@5: } nuclear@5: } nuclear@5: nuclear@5: if(func) { nuclear@5: /* ... and then call the idle function */ nuclear@5: func(); nuclear@5: } nuclear@5: nuclear@5: activate_window(prev_active); nuclear@5: return 0; nuclear@5: } nuclear@5: nuclear@5: /* returns 0, or -1 when the last window gets closed */ nuclear@5: static int handle_event(XEvent *xev) nuclear@5: { nuclear@5: int state; nuclear@5: struct window *win; nuclear@5: void (*func)(); nuclear@7: KeySym sym; nuclear@5: nuclear@5: if((win = find_window(xev->xany.window))) { nuclear@5: activate_window(win); nuclear@5: } else { nuclear@5: return 0; nuclear@5: } nuclear@5: nuclear@5: switch(xev->type) { nuclear@5: case MapNotify: nuclear@5: active_win->mapped = 1; nuclear@5: break; nuclear@5: nuclear@5: case UnmapNotify: nuclear@5: active_win->mapped = 0; nuclear@5: break; nuclear@5: nuclear@5: case Expose: nuclear@5: if(active_win->mapped && xev->xexpose.count == 0) { nuclear@5: if((func = sgl_get_callback(SGL_DISPLAY))) { nuclear@5: func(); nuclear@5: active_win->redisp_pending = 0; nuclear@5: } nuclear@5: } nuclear@5: break; nuclear@5: nuclear@5: case MotionNotify: nuclear@5: if(xev->xmotion.state) { nuclear@5: func = sgl_get_callback(SGL_MOTION); nuclear@5: } else { nuclear@5: func = sgl_get_callback(SGL_PASSIVE); nuclear@5: } nuclear@5: if(func) { nuclear@6: func(0, xev->xmotion.x, xev->xmotion.y); nuclear@5: } nuclear@5: break; nuclear@5: nuclear@5: case ButtonPress: nuclear@5: if(1) { nuclear@5: state = 1; nuclear@5: } else { nuclear@5: case ButtonRelease: nuclear@5: state = 0; nuclear@5: } nuclear@5: if((func = sgl_get_callback(SGL_MOUSE))) { nuclear@5: int bn = xev->xbutton.button - 1; nuclear@6: func(0, bn, state, xev->xbutton.x, xev->xbutton.y); nuclear@5: } nuclear@5: break; nuclear@5: nuclear@5: case KeyPress: nuclear@5: if(1) { nuclear@5: state = 1; nuclear@5: } else { nuclear@5: case KeyRelease: nuclear@5: state = 0; nuclear@5: } nuclear@7: sym = XLookupKeysym(&xev->xkey, 0); nuclear@7: process_key(sym, state); nuclear@7: nuclear@5: if((func = sgl_get_callback(SGL_KEYBOARD))) { nuclear@5: func(translate_keysym(sym), state); nuclear@5: } nuclear@5: break; nuclear@5: nuclear@5: case ConfigureNotify: nuclear@5: if((func = sgl_get_callback(SGL_RESHAPE))) { nuclear@5: if(xev->xconfigure.width != active_win->width || xev->xconfigure.height != active_win->height) { nuclear@5: active_win->width = xev->xconfigure.width; nuclear@5: active_win->height = xev->xconfigure.height; nuclear@5: nuclear@5: func(xev->xconfigure.width, xev->xconfigure.height); nuclear@5: } nuclear@5: } nuclear@5: break; nuclear@5: nuclear@5: case ClientMessage: nuclear@5: if(xev->xclient.message_type == xa_wm_prot) { nuclear@5: if(xev->xclient.data.l[0] == xa_wm_del_win) { nuclear@5: close_window(active_win->win); nuclear@5: if(!active_win) { nuclear@5: return -1; nuclear@4: } nuclear@4: } nuclear@5: } nuclear@5: break; nuclear@4: nuclear@5: default: nuclear@5: break; nuclear@5: } nuclear@4: nuclear@4: return 0; nuclear@4: } nuclear@5: nuclear@7: static void process_key(KeySym sym, int state) nuclear@7: { nuclear@7: switch(sym) { nuclear@7: case XK_Shift_L: nuclear@7: case XK_Shift_R: nuclear@7: modkeys = state ? (modkeys | SGL_MOD_SHIFT) : (modkeys & ~SGL_MOD_SHIFT); nuclear@7: break; nuclear@7: nuclear@7: case XK_Control_L: nuclear@7: case XK_Control_R: nuclear@7: modkeys = state ? (modkeys | SGL_MOD_CONTROL) : (modkeys & ~SGL_MOD_CONTROL); nuclear@7: break; nuclear@7: nuclear@7: case XK_Alt_L: nuclear@7: case XK_Alt_R: nuclear@7: modkeys = state ? (modkeys | SGL_MOD_ALT) : (modkeys & ~SGL_MOD_ALT); nuclear@7: break; nuclear@7: nuclear@7: default: nuclear@7: break; nuclear@7: } nuclear@7: } nuclear@7: nuclear@5: static int translate_keysym(KeySym sym) nuclear@5: { nuclear@5: if(sym < 256) { nuclear@7: if(isalpha(sym) && (modkeys & SGL_MOD_SHIFT)) { nuclear@7: sym = toupper(sym); nuclear@7: } nuclear@5: return sym; nuclear@5: } nuclear@5: nuclear@5: switch(sym) { nuclear@5: case XK_BackSpace: nuclear@5: return '\b'; nuclear@5: case XK_Tab: nuclear@5: return '\t'; nuclear@5: case XK_Linefeed: nuclear@5: return '\r'; nuclear@5: case XK_Return: nuclear@5: return '\n'; nuclear@5: case XK_Escape: nuclear@5: return 27; nuclear@5: default: nuclear@5: break; nuclear@5: } nuclear@5: return (int)sym; nuclear@5: } nuclear@7: nuclear@8: #else nuclear@8: int sgl_wsys_x11_silence_the_fucking_empty_file_warnings; nuclear@7: #endif /* USE_WSYS_MODULE_X11 */