# HG changeset patch # User John Tsiombikas # Date 1305187450 -10800 # Node ID 648f8604d2b2510c1d627f6263823fdaf597218e # Parent 1b6c5dadb46091aa14ef9782600eb752367e3df3 cont. x11 module diff -r 1b6c5dadb460 -r 648f8604d2b2 include/sgl.h --- a/include/sgl.h Wed May 11 09:09:43 2011 +0300 +++ b/include/sgl.h Thu May 12 11:04:10 2011 +0300 @@ -5,6 +5,7 @@ #define SGL_DEPTH 2 #define SGL_STENCIL 4 #define SGL_STEREO 8 +#define SGL_MULTISAMPLE 16 enum { SGL_CREATE, @@ -23,15 +24,22 @@ int sgl_init(void); void sgl_shutdown(void); -int sgl_set_video_mode(int xsz, int ysz, int bpp); -int sgl_get_video_mode(int *xsz, int *ysz, int *bpp); +int sgl_set_video_mode(int xsz, int ysz); +int sgl_get_video_mode(int *xsz, int *ysz); -int sgl_window(int xsz, int ysz, unsigned int mode); +int sgl_create_window(int xsz, int ysz, unsigned int mode); void sgl_close_window(int win); +int sgl_set_active(int id); +int sgl_set_title(const char *str); + +int sgl_process_events(void); +int sgl_event_loop(void); + int sgl_push_callbacks(void); int sgl_pop_callbacks(void); void sgl_clear_callbacks(void); -void (*sgl_callback(int idx, void (*func)()))(); +void sgl_set_callback(int idx, void (*func)()); +void (*sgl_get_callback(int idx))(); #endif /* SGL_H_ */ diff -r 1b6c5dadb460 -r 648f8604d2b2 src/cb.c --- a/src/cb.c Wed May 11 09:09:43 2011 +0300 +++ b/src/cb.c Thu May 12 11:04:10 2011 +0300 @@ -1,12 +1,15 @@ #include #include #include "sgl.h" +#include "wsys.h" struct cbnode { void (*func[SGL_NUM_CALLBACKS])(); struct cbnode *next; }; +static void notify_wsys(void); + struct cbnode first_cbnode; struct cbnode *cb = &first_cbnode; @@ -34,17 +37,36 @@ node = cb; cb = cb->next; free(node); + + notify_wsys(); return 0; } void sgl_clear_callbacks(void) { memset(cb->func, 0, sizeof cb->func); + notify_wsys(); } -void (*sgl_callback(int idx, void (*func)()))() +void sgl_callback(int idx, void (*func)()) { - void (*prev)() = cb->func[idx]; cb->func[idx] = func; - return prev; } + +void (*sgl_get_callback(int idx))() +{ + return cb->func[idx]; +} + +/* notify the window system module as to which events are active */ +static void notify_wsys(void) +{ + int i; + struct wsys_module *ws; + + if((ws = sgl_wsys_module())) { + for(i=0; iset_event(i, cb->func[i] != 0); + } + } +} diff -r 1b6c5dadb460 -r 648f8604d2b2 src/log.c --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/log.c Thu May 12 11:04:10 2011 +0300 @@ -0,0 +1,21 @@ +#include +#include +#include +#include "log.h" + +void sgl_log(const char *fmt, ...) +{ + va_list ap; + const char *logfile; + FILE *fp; + + if(!(logfile = getenv("SGL_LOG")) || !(fp = fopen(logfile, "a"))) { + fp = stderr; + } + + va_start(ap, fmt); + vfprintf(fp, fmt, ap); + va_end(ap); + + fclose(fp); +} diff -r 1b6c5dadb460 -r 648f8604d2b2 src/log.h --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/log.h Thu May 12 11:04:10 2011 +0300 @@ -0,0 +1,6 @@ +#ifndef LOG_H_ +#define LOG_H_ + +void sgl_log(const char *fmt, ...); + +#endif /* LOG_H_ */ diff -r 1b6c5dadb460 -r 648f8604d2b2 src/sgl.c --- a/src/sgl.c Wed May 11 09:09:43 2011 +0300 +++ b/src/sgl.c Thu May 12 11:04:10 2011 +0300 @@ -1,29 +1,57 @@ #include "sgl.h" +#include "wsys.h" void sgl_register_modules(void); +static struct wsys_module *ws; + int sgl_init(void) { sgl_register_modules(); sgl_sort_modules(); - return 0; + + if(!(ws = sgl_wsys_module())) { + return -1; + } + return ws->init(); } -int sgl_set_video_mode(int xsz, int ysz, int bpp) +void sgl_shutdown(void) { - return 0; + ws->shutdown(); } -int sgl_get_video_mode(int *xsz, int *ysz, int *bpp) +int sgl_set_video_mode(int xsz, int ysz) { - return 0; + return ws->set_vidmode(xsz, ysz); } -int sgl_window(int x, int y, unsigned int mode) +int sgl_get_video_mode(int *xsz, int *ysz) { - return 0; + return ws->get_vidmode(xsz, ysz); } -void sgl_close(int win) +int sgl_create_window(int xsz, int ysz, unsigned int mode) { + return ws->create_window(xsz, ysz, mode); } + +void sgl_close_window(int win) +{ + ws->close_window(win); +} + +int sgl_set_active(int id) +{ + return ws->set_active(id); +} + +int sgl_set_title(const char *str) +{ + return ws->set_title(str); +} + +int sgl_process_events(void) +{ + return ws->process_events(); +} diff -r 1b6c5dadb460 -r 648f8604d2b2 src/wsys.h --- a/src/wsys.h Wed May 11 09:09:43 2011 +0300 +++ b/src/wsys.h Thu May 12 11:04:10 2011 +0300 @@ -8,11 +8,21 @@ int (*init)(void); void (*shutdown)(void); - int (*set_vidmode)(int, int, int); - int (*get_vidmode)(int*, int*, int*); + int (*set_vidmode)(int, int); + int (*get_vidmode)(int*, int*); + int (*create_window)(int, int, unsigned int); void (*close_window)(int); + int (*set_active)(int); + int (*set_title)(const char*); + + void (*set_event)(int idx, int enable); + int (*process_events)(void); + + void (*redisplay)(void); + void (*swap_buffers)(void); + struct wsys_module *next; }; diff -r 1b6c5dadb460 -r 648f8604d2b2 src/wsys_x11.c --- a/src/wsys_x11.c Wed May 11 09:09:43 2011 +0300 +++ b/src/wsys_x11.c Thu May 12 11:04:10 2011 +0300 @@ -1,20 +1,43 @@ +#include #include #include +#include "sgl.h" #include "wsys.h" +#include "log.h" struct window { Window win; GLXContext ctx; + long evmask; struct window *next; }; static int init(void); static void shutdown(void); -static int set_vidmode(int xsz, int ysz, int bpp); -static int get_vidmode(int *xsz, int *ysz, int *bpp); + +/* video mode switching */ +static int set_vidmode(int xsz, int ysz); +static int get_vidmode(int *xsz, int *ysz); + +/* create/destroy windows */ static int create_window(int xsz, int ysz, unsigned int flags); +static void fill_attr(int *attr, unsigned int flags); +static void print_visual_info(XVisualInfo *vis); static void close_window(int id); +/* window management */ +static int set_active(int id); +static int set_title(const char *str); +static void redisplay(void); +static void swap_buffers(void); + +/* event handling and friends */ +static void set_bits(long *mask, long bits); +static void clear_bits(long *mask, long bits); +static void set_event(int idx, int enable); +static int process_events(void); + + static struct wsys_module ws = { "x11-glx", 0, init, @@ -23,14 +46,23 @@ get_vidmode, create_window, close_window, + set_active, + set_title, + redisplay, + swap_buffers, + set_event, + process_events, 0 }; static Display *dpy; +static Window root; static int scr; +static Atom xa_wm_prot, xa_wm_del_win; static struct window *winlist; +static struct window *active_win; - +/* this is the only exported function, everything else should be static */ void sgl_register_x11(void) { sgl_register_module(&ws); @@ -41,9 +73,14 @@ winlist = 0; if(!(dpy = XOpenDisplay(0))) { + sgl_log("failed to open X display: %s\n", XDisplayName(0)); return -1; } scr = DefaultScreen(dpy); + root = RootWindow(dpy, scr); + + xa_wm_prot = XInternAtom(dpy, "WM_PROTOCOLS", False); + xa_wm_del_win = XInternAtom(dpy, "WM_DELETE_WINDOW", False); return 0; } @@ -55,25 +92,151 @@ winlist = winlist->next; glXDestroyContext(dpy, win->ctx); - XCloseWindow(dpy, win->win); + XDestroyWindow(dpy, win->win); free(win); } XCloseDisplay(dpy); dpy = 0; } -static int set_vidmode(int xsz, int ysz, int bpp) +static int set_vidmode(int xsz, int ysz) { /* TODO */ + return 0; } -static int get_vidmode(int *xsz, int *ysz, int *bpp) +static int get_vidmode(int *xsz, int *ysz) { /* TODO */ + return 0; } static int create_window(int xsz, int ysz, unsigned int flags) { + int attr[32]; + Window win; + GLXContext ctx; + XVisualInfo *vis; + XClassHint chint; + XSetWindowAttributes xattr; + unsigned int attr_valid; + long evmask; + struct window *wnode; + + if(!(wnode = malloc(sizeof *wnode))) { + return -1; + } + + fill_attr(attr, flags); + + if(!(vis = glXChooseVisual(dpy, scr, attr))) { + sgl_log("failed to find a suitable visual\n"); + free(wnode); + return -1; + } + print_visual_info(vis); + + if(!(ctx = glXCreateContext(dpy, vis, 0, True))) { + sgl_log("failed to create OpenGL context\n"); + XFree(vis); + free(wnode); + return -1; + } + + xattr.background_pixel = xattr.border_pixel = BlackPixel(dpy, scr); + xattr.colormap = XCreateColormap(dpy, root, vis->visual, AllocNone); + attr_valid = CWColormap | CWBackPixel | CWBorderPixel; + + if(!(win = XCreateWindow(dpy, root, 0, 0, xsz, ysz, 0, vis->depth, InputOutput, + vis->visual, attr_valid, &xattr))) { + sgl_log("failed to create window\n"); + glXDestroyContext(dpy, ctx); + XFree(vis); + free(wnode); + return -1; + } + XFree(vis); + + evmask = StructureNotifyMask | VisibilityChangeMask; + XSelectInput(dpy, win, evmask); + + XSetWMProtocols(dpy, win, &xa_wm_del_win, 1); + + set_title("OpenGL/X11"); + + chint.res_name = chint.res_class = "simplygl"; + XSetClassHint(dpy, win, &chint); + + XMapWindow(dpy, win); + + wnode->win = win; + wnode->ctx = ctx; + wnode->evmask = evmask; + wnode->next = winlist; + winlist->next = wnode; + + if(!active_win) { + set_active(win); + } + return win; +} + +static void fill_attr(int *attr, unsigned int flags) +{ + int i = 0; + + attr[i++] = GLX_RGBA; + attr[i++] = GLX_RED_SIZE; attr[i++] = 1; + attr[i++] = GLX_GREEN_SIZE; attr[i++] = 1; + attr[i++] = GLX_BLUE_SIZE; attr[i++] = 1; + + if(flags & SGL_DOUBLE) { + attr[i++] = GLX_DOUBLEBUFFER; + } + if(flags & SGL_DEPTH) { + attr[i++] = GLX_DEPTH_SIZE; + attr[i++] = 1; + } + if(flags & SGL_STENCIL) { + attr[i++] = GLX_STENCIL_SIZE; + attr[i++] = 1; + } + if(flags & SGL_STEREO) { + attr[i++] = GLX_STEREO; + } +#if defined(GLX_VERSION_1_4) || defined(GLX_ARB_multisample) + if(flags & SGL_MULTISAMPLE) { + attr[i++] = GLX_SAMPLE_BUFFERS_ARB; + attr[i++] = 1; + attr[i++] = GLX_SAMPLES_ARB; + attr[i++] = 1; + } +#endif /* defined GLX_VERSION_1_4 || GLX_ARB_multisample */ + attr[i] = None; +} + +static void print_visual_info(XVisualInfo *vis) +{ + int rbits, gbits, bbits, zbits, sbits, stereo, aa, samples; + + glXGetConfig(dpy, vis, GLX_RED_SIZE, &rbits); + glXGetConfig(dpy, vis, GLX_GREEN_SIZE, &gbits); + glXGetConfig(dpy, vis, GLX_BLUE_SIZE, &bbits); + glXGetConfig(dpy, vis, GLX_DEPTH_SIZE, &zbits); + glXGetConfig(dpy, vis, GLX_STENCIL_SIZE, &sbits); + glXGetConfig(dpy, vis, GLX_STEREO, &stereo); +#if defined(GLX_VERSION_1_4) || defined(GLX_ARB_multisample) + glXGetConfig(dpy, vis, GLX_SAMPLE_BUFFERS_ARB, &aa); + if(aa) { + glXGetConfig(dpy, vis, GLX_SAMPLES_ARB, &samples); + } else { + samples = 1; + } +#endif /* defined GLX_VERSION_1_4 || GLX_ARB_multisample */ + + sgl_log("got visual: %d%d%d d:%d s:%d", rbits, gbits, bbits, zbits, sbits); + sgl_log(" %s", stereo ? "stereo" : "mono"); + sgl_log(" samples/pixel: %d\n", samples); } static void close_window(int id) @@ -87,7 +250,7 @@ while(win) { if(win->win == id) { glXDestroyContext(dpy, win->ctx); - XCloseWindow(dpy, win->win); + XDestroyWindow(dpy, win->win); prev->next = win->next; free(win); return; @@ -95,3 +258,168 @@ win = win->next; } } + +static int set_active(int id) +{ + struct window *win = winlist; + + while(win) { + if(win->win == id) { + if(glXMakeCurrent(dpy, win->win, win->ctx) == False) { + sgl_log("failed to activate window %d\n", id); + return -1; + } + active_win = win; + return 0; + } + win = win->next; + } + + sgl_log("no such window: %d\n", id); + return -1; +} + +static int set_title(const char *str) +{ + XTextProperty wm_name; + + if(!str || !active_win) { + return -1; + } + XStringListToTextProperty((char**)&str, 1, &wm_name); + XSetWMName(dpy, active_win->win, &wm_name); + XSetWMIconName(dpy, active_win->win, &wm_name); + XFree(wm_name.value); + return 0; +} + +static void redisplay(void) +{ + active_win->redisp_pending = 1; +} + +static void swap_buffers(void) +{ + glXSwapBuffers(dpy, active_win->ctx); +} + +static void set_bits(long *mask, long bits) +{ + *mask |= bits; +} + +static void clear_bits(long *mask, long bits) +{ + *mask &= ~bits; +} + +static void set_event(int idx, int enable) +{ + void (*op)(long*, long); + op = enable ? set_bits : clear_bits; + + switch(idx) { + case SGL_DISPLAY: + op(&active_win->evmask, ExposureMask); + break; + + case SGL_KEYBOARD: + op(&active_win->evmask, KeyPressMask | KeyReleaseMask); + break; + + case SGL_MOUSE: + op(&active_win->evmask, ButtonPressMask | ButtonReleaseMask); + break; + + case SGL_MOTION: + op(&active_win->evmask, ButtonMotionMask); + break; + + case SGL_PASSIVE: + op(&active_win->evmask, PointerMotionMask); + break; + + default: + return; + } + + XSelectInput(dpy, active_win->win, active_win->evmask); +} + +static int process_events(void) +{ + XEvent xev; + void (*func)(); + int state; + + while(XPending(dpy)) { + XNextEvent(dpy, &xev); + + switch(xev.type) { + case Expose: + if(xev.xexpose.count == 0) { + if((func = sgl_get_callback(SGL_DISPLAY))) { + func(); + active_win->redisp_pending = 0; + } + } + break; + + case MotionNotify: + if(xev.xmotion.state) { + func = sgl_get_callback(SGL_MOTION); + } else { + func = sgl_get_callback(SGL_PASSIVE); + } + if(func) { + func(xev.xmotion.x, xev.xmotion.y); + } + break; + + case ButtonPress: + if(1) { + state = 1; + } else { + case ButtonRelease: + state = 0; + } + if((func = sgl_get_callback(SGL_MOUSE))) { + int bn = xev.xbutton.button - 1; + func(bn, state, xev.xbutton.x, xev.xbutton.y); + } + break; + + case KeyPress: + if(1) { + state = 1; + } else { + case KeyRelease: + state = 0; + } + if((func = sgl_get_callback(SGL_KEYBOARD))) { + KeySym sym = XLookupKeysym(xev.xkey, 0); + func(sym, state); + /* XXX */ + } + break; + + case ConfigureNotify: + if((func = sgl_get_callback(SGL_RESHAPE))) { + func(xev.xconfigure.width, xev.xconfigure.height); + } + break; + } + + case ClientMessage: + if(xev.xclient.message_type == xa_wm_prot) { + if(xev.xclient.data.l[0] == xa_wm_del_win) { + close_window(active_win->win); + if(!active_win) { + return 1; + } + } + } + break; + } + return 0; +}