|
|
@@ -55,62 +55,139 @@ struct PuglInternalsImpl { |
|
|
|
#endif |
|
|
|
}; |
|
|
|
|
|
|
|
/** |
|
|
|
Attributes for single-buffered RGBA with at least |
|
|
|
4 bits per color and a 16 bit depth buffer. |
|
|
|
*/ |
|
|
|
static int attrListSgl[] = { |
|
|
|
GLX_RGBA, |
|
|
|
GLX_RED_SIZE, 4, |
|
|
|
GLX_GREEN_SIZE, 4, |
|
|
|
GLX_BLUE_SIZE, 4, |
|
|
|
GLX_DEPTH_SIZE, 16, |
|
|
|
None |
|
|
|
}; |
|
|
|
|
|
|
|
/** |
|
|
|
Attributes for double-buffered RGBA with at least |
|
|
|
4 bits per color and a 16 bit depth buffer. |
|
|
|
*/ |
|
|
|
static int attrListDbl[] = { |
|
|
|
GLX_RGBA, GLX_DOUBLEBUFFER, |
|
|
|
GLX_RED_SIZE, 4, |
|
|
|
GLX_GREEN_SIZE, 4, |
|
|
|
GLX_BLUE_SIZE, 4, |
|
|
|
GLX_DEPTH_SIZE, 16, |
|
|
|
None |
|
|
|
}; |
|
|
|
|
|
|
|
PuglInternals* |
|
|
|
puglInitInternals() |
|
|
|
{ |
|
|
|
return (PuglInternals*)calloc(1, sizeof(PuglInternals)); |
|
|
|
} |
|
|
|
|
|
|
|
static XVisualInfo* |
|
|
|
getVisual(PuglView* view) |
|
|
|
{ |
|
|
|
PuglInternals* const impl = view->impl; |
|
|
|
XVisualInfo* vi = NULL; |
|
|
|
|
|
|
|
#ifdef PUGL_HAVE_GL |
|
|
|
if (view->ctx_type == PUGL_GL) { |
|
|
|
// Try to create double-buffered visual |
|
|
|
int double_attrs[] = { GLX_RGBA, GLX_DOUBLEBUFFER, |
|
|
|
GLX_RED_SIZE, 4, |
|
|
|
GLX_GREEN_SIZE, 4, |
|
|
|
GLX_BLUE_SIZE, 4, |
|
|
|
GLX_DEPTH_SIZE, 16, |
|
|
|
None }; |
|
|
|
vi = glXChooseVisual(impl->display, impl->screen, double_attrs); |
|
|
|
if (!vi) { |
|
|
|
// Failed, create single-buffered visual |
|
|
|
int single_attrs[] = { GLX_RGBA, |
|
|
|
GLX_RED_SIZE, 4, |
|
|
|
GLX_GREEN_SIZE, 4, |
|
|
|
GLX_BLUE_SIZE, 4, |
|
|
|
GLX_DEPTH_SIZE, 16, |
|
|
|
None }; |
|
|
|
vi = glXChooseVisual(impl->display, impl->screen, single_attrs); |
|
|
|
impl->doubleBuffered = False; |
|
|
|
} else { |
|
|
|
impl->doubleBuffered = True; |
|
|
|
} |
|
|
|
#ifdef PUGL_VERBOSE |
|
|
|
int glxMajor, glxMinor; |
|
|
|
glXQueryVersion(impl->display, &glxMajor, &glxMinor); |
|
|
|
PUGL_LOGF("GLX Version %d.%d\n", glxMajor, glxMinor); |
|
|
|
#endif |
|
|
|
} |
|
|
|
#endif |
|
|
|
#ifdef PUGL_HAVE_CAIRO |
|
|
|
if (view->ctx_type == PUGL_CAIRO) { |
|
|
|
XVisualInfo pat; |
|
|
|
int n; |
|
|
|
pat.screen = impl->screen; |
|
|
|
vi = XGetVisualInfo(impl->display, VisualScreenMask, &pat, &n); |
|
|
|
} |
|
|
|
#endif |
|
|
|
|
|
|
|
return vi; |
|
|
|
} |
|
|
|
|
|
|
|
static void |
|
|
|
createContext(PuglView* view, XVisualInfo* vi) |
|
|
|
{ |
|
|
|
PuglInternals* const impl = view->impl; |
|
|
|
|
|
|
|
#ifdef PUGL_HAVE_GL |
|
|
|
if (view->ctx_type == PUGL_GL) { |
|
|
|
impl->ctx = glXCreateContext(impl->display, vi, 0, GL_TRUE); |
|
|
|
#ifdef PUGL_VERBOSE |
|
|
|
if (glXIsDirect(impl->display, impl->ctx)) { |
|
|
|
PUGL_LOG("DRI enabled (to disable, set LIBGL_ALWAYS_INDIRECT=1\n"); |
|
|
|
} else { |
|
|
|
PUGL_LOG("No DRI available\n"); |
|
|
|
} |
|
|
|
#endif |
|
|
|
} |
|
|
|
#endif |
|
|
|
#ifdef PUGL_HAVE_CAIRO |
|
|
|
if (view->ctx_type == PUGL_CAIRO) { |
|
|
|
cairo_surface_t* surface = cairo_xlib_surface_create( |
|
|
|
impl->display, impl->win, vi->visual, view->width, view->height); |
|
|
|
if (!(impl->cr = cairo_create(surface))) { |
|
|
|
fprintf(stderr, "failed to create cairo context\n"); |
|
|
|
} |
|
|
|
} |
|
|
|
#endif |
|
|
|
} |
|
|
|
|
|
|
|
static void |
|
|
|
destroyContext(PuglView* view) |
|
|
|
{ |
|
|
|
#ifdef PUGL_HAVE_GL |
|
|
|
if (view->ctx_type == PUGL_GL) { |
|
|
|
glXDestroyContext(view->impl->display, view->impl->ctx); |
|
|
|
} |
|
|
|
#endif |
|
|
|
#ifdef PUGL_HAVE_CAIRO |
|
|
|
if (view->ctx_type == PUGL_CAIRO) { |
|
|
|
glXDestroyContext(view->impl->display, view->impl->ctx); |
|
|
|
} |
|
|
|
#endif |
|
|
|
} |
|
|
|
|
|
|
|
void |
|
|
|
puglEnterContext(PuglView* view) |
|
|
|
{ |
|
|
|
#ifdef PUGL_HAVE_GL |
|
|
|
if (view->ctx_type == PUGL_GL) { |
|
|
|
glXMakeCurrent(view->impl->display, view->impl->win, view->impl->ctx); |
|
|
|
} |
|
|
|
#endif |
|
|
|
} |
|
|
|
|
|
|
|
void |
|
|
|
puglLeaveContext(PuglView* view, bool flush) |
|
|
|
{ |
|
|
|
#ifdef PUGL_HAVE_GL |
|
|
|
if (view->ctx_type == PUGL_GL && flush) { |
|
|
|
glFlush(); |
|
|
|
if (view->impl->doubleBuffered) { |
|
|
|
glXSwapBuffers(view->impl->display, view->impl->win); |
|
|
|
} |
|
|
|
} |
|
|
|
#endif |
|
|
|
} |
|
|
|
|
|
|
|
int |
|
|
|
puglCreateWindow(PuglView* view, const char* title) |
|
|
|
{ |
|
|
|
PuglInternals* impl = view->impl; |
|
|
|
PuglInternals* const impl = view->impl; |
|
|
|
|
|
|
|
impl->display = XOpenDisplay(0); |
|
|
|
impl->screen = DefaultScreen(impl->display); |
|
|
|
|
|
|
|
XVisualInfo* vi = glXChooseVisual(impl->display, impl->screen, attrListDbl); |
|
|
|
XVisualInfo* const vi = getVisual(view); |
|
|
|
if (!vi) { |
|
|
|
vi = glXChooseVisual(impl->display, impl->screen, attrListSgl); |
|
|
|
impl->doubleBuffered = False; |
|
|
|
PUGL_LOG("No double buffering available\n"); |
|
|
|
} else { |
|
|
|
impl->doubleBuffered = True; |
|
|
|
PUGL_LOG("Double buffered rendering enabled\n"); |
|
|
|
return 1; |
|
|
|
} |
|
|
|
|
|
|
|
int glxMajor, glxMinor; |
|
|
|
glXQueryVersion(impl->display, &glxMajor, &glxMinor); |
|
|
|
PUGL_LOGF("GLX Version %d.%d\n", glxMajor, glxMinor); |
|
|
|
|
|
|
|
impl->ctx = glXCreateContext(impl->display, vi, 0, GL_TRUE); |
|
|
|
|
|
|
|
Window xParent = view->parent |
|
|
|
? (Window)view->parent |
|
|
|
: RootWindow(impl->display, impl->screen); |
|
|
@@ -120,20 +197,21 @@ puglCreateWindow(PuglView* view, const char* title) |
|
|
|
|
|
|
|
XSetWindowAttributes attr; |
|
|
|
memset(&attr, 0, sizeof(XSetWindowAttributes)); |
|
|
|
attr.colormap = cmap; |
|
|
|
attr.border_pixel = 0; |
|
|
|
|
|
|
|
attr.event_mask = ExposureMask | KeyPressMask | KeyReleaseMask |
|
|
|
| ButtonPressMask | ButtonReleaseMask |
|
|
|
#ifdef PUGL_GRAB_FOCUS |
|
|
|
| EnterWindowMask |
|
|
|
#endif |
|
|
|
| PointerMotionMask | StructureNotifyMask; |
|
|
|
attr.background_pixel = BlackPixel(impl->display, impl->screen); |
|
|
|
attr.border_pixel = BlackPixel(impl->display, impl->screen); |
|
|
|
attr.colormap = cmap; |
|
|
|
attr.event_mask = (ExposureMask | StructureNotifyMask | |
|
|
|
EnterWindowMask | LeaveWindowMask | |
|
|
|
KeyPressMask | KeyReleaseMask | |
|
|
|
ButtonPressMask | ButtonReleaseMask | |
|
|
|
PointerMotionMask); |
|
|
|
|
|
|
|
impl->win = XCreateWindow( |
|
|
|
impl->display, xParent, |
|
|
|
0, 0, view->width, view->height, 0, vi->depth, InputOutput, vi->visual, |
|
|
|
CWBorderPixel | CWColormap | CWEventMask, &attr); |
|
|
|
CWBackPixel | CWBorderPixel | CWColormap | CWEventMask, &attr); |
|
|
|
|
|
|
|
createContext(view, vi); |
|
|
|
|
|
|
|
XSizeHints sizeHints; |
|
|
|
memset(&sizeHints, 0, sizeof(sizeHints)); |
|
|
@@ -155,33 +233,21 @@ puglCreateWindow(PuglView* view, const char* title) |
|
|
|
XSetWMProtocols(impl->display, impl->win, &wmDelete, 1); |
|
|
|
} |
|
|
|
|
|
|
|
if (glXIsDirect(impl->display, impl->ctx)) { |
|
|
|
PUGL_LOG("DRI enabled (to disable, set LIBGL_ALWAYS_INDIRECT=1\n"); |
|
|
|
} else { |
|
|
|
PUGL_LOG("No DRI available\n"); |
|
|
|
} |
|
|
|
|
|
|
|
XFree(vi); |
|
|
|
|
|
|
|
glXMakeCurrent(view->impl->display, view->impl->win, view->impl->ctx); |
|
|
|
|
|
|
|
return 0; |
|
|
|
} |
|
|
|
|
|
|
|
void |
|
|
|
puglShowWindow(PuglView* view) |
|
|
|
{ |
|
|
|
PuglInternals* impl = view->impl; |
|
|
|
|
|
|
|
XMapRaised(impl->display, impl->win); |
|
|
|
XMapRaised(view->impl->display, view->impl->win); |
|
|
|
} |
|
|
|
|
|
|
|
void |
|
|
|
puglHideWindow(PuglView* view) |
|
|
|
{ |
|
|
|
PuglInternals* impl = view->impl; |
|
|
|
|
|
|
|
XUnmapWindow(impl->display, impl->win); |
|
|
|
XUnmapWindow(view->impl->display, view->impl->win); |
|
|
|
} |
|
|
|
|
|
|
|
void |
|
|
@@ -191,43 +257,13 @@ puglDestroy(PuglView* view) |
|
|
|
return; |
|
|
|
} |
|
|
|
|
|
|
|
glXDestroyContext(view->impl->display, view->impl->ctx); |
|
|
|
destroyContext(view); |
|
|
|
XDestroyWindow(view->impl->display, view->impl->win); |
|
|
|
XCloseDisplay(view->impl->display); |
|
|
|
free(view->impl); |
|
|
|
free(view); |
|
|
|
} |
|
|
|
|
|
|
|
static void |
|
|
|
puglReshape(PuglView* view, int width, int height) |
|
|
|
{ |
|
|
|
glXMakeCurrent(view->impl->display, view->impl->win, view->impl->ctx); |
|
|
|
|
|
|
|
if (view->reshapeFunc) { |
|
|
|
view->reshapeFunc(view, width, height); |
|
|
|
} |
|
|
|
|
|
|
|
view->width = width; |
|
|
|
view->height = height; |
|
|
|
} |
|
|
|
|
|
|
|
static void |
|
|
|
puglDisplay(PuglView* view) |
|
|
|
{ |
|
|
|
glXMakeCurrent(view->impl->display, view->impl->win, view->impl->ctx); |
|
|
|
|
|
|
|
if (view->displayFunc) { |
|
|
|
view->displayFunc(view); |
|
|
|
} |
|
|
|
|
|
|
|
glFlush(); |
|
|
|
if (view->impl->doubleBuffered) { |
|
|
|
glXSwapBuffers(view->impl->display, view->impl->win); |
|
|
|
} |
|
|
|
|
|
|
|
view->redisplay = false; |
|
|
|
} |
|
|
|
|
|
|
|
static PuglKey |
|
|
|
keySymToSpecial(KeySym sym) |
|
|
|
{ |
|
|
@@ -266,139 +302,184 @@ keySymToSpecial(KeySym sym) |
|
|
|
} |
|
|
|
|
|
|
|
static void |
|
|
|
setModifiers(PuglView* view, unsigned xstate, unsigned xtime) |
|
|
|
translateKey(XEvent* xevent, PuglEvent* event) |
|
|
|
{ |
|
|
|
view->event_timestamp_ms = xtime; |
|
|
|
KeySym sym; |
|
|
|
char str[5]; |
|
|
|
const int n = XLookupString(&xevent->xkey, str, 4, &sym, NULL); |
|
|
|
if (n == 1) { |
|
|
|
event->key.character = str[0]; // TODO: multi-byte support |
|
|
|
} |
|
|
|
event->key.special = keySymToSpecial(sym); |
|
|
|
} |
|
|
|
|
|
|
|
view->mods = 0; |
|
|
|
view->mods |= (xstate & ShiftMask) ? PUGL_MOD_SHIFT : 0; |
|
|
|
view->mods |= (xstate & ControlMask) ? PUGL_MOD_CTRL : 0; |
|
|
|
view->mods |= (xstate & Mod1Mask) ? PUGL_MOD_ALT : 0; |
|
|
|
view->mods |= (xstate & Mod4Mask) ? PUGL_MOD_SUPER : 0; |
|
|
|
static unsigned |
|
|
|
translateModifiers(unsigned xstate) |
|
|
|
{ |
|
|
|
unsigned state = 0; |
|
|
|
state |= (xstate & ShiftMask) ? PUGL_MOD_SHIFT : 0; |
|
|
|
state |= (xstate & ControlMask) ? PUGL_MOD_CTRL : 0; |
|
|
|
state |= (xstate & Mod1Mask) ? PUGL_MOD_ALT : 0; |
|
|
|
state |= (xstate & Mod4Mask) ? PUGL_MOD_SUPER : 0; |
|
|
|
return state; |
|
|
|
} |
|
|
|
|
|
|
|
static void |
|
|
|
dispatchKey(PuglView* view, XEvent* event, bool press) |
|
|
|
static PuglEvent |
|
|
|
translateEvent(PuglView* view, XEvent xevent) |
|
|
|
{ |
|
|
|
KeySym sym; |
|
|
|
char str[5]; |
|
|
|
const int n = XLookupString(&event->xkey, str, 4, &sym, NULL); |
|
|
|
if (n == 0) { |
|
|
|
return; |
|
|
|
} else if (n > 1) { |
|
|
|
fprintf(stderr, "warning: Unsupported multi-byte key %X\n", (int)sym); |
|
|
|
return; |
|
|
|
PuglEvent event; |
|
|
|
memset(&event, 0, sizeof(event)); |
|
|
|
|
|
|
|
event.any.view = view; |
|
|
|
event.any.send_event = xevent.xany.send_event; |
|
|
|
|
|
|
|
switch (xevent.type) { |
|
|
|
case ConfigureNotify: |
|
|
|
event.type = PUGL_CONFIGURE; |
|
|
|
event.configure.x = xevent.xconfigure.x; |
|
|
|
event.configure.y = xevent.xconfigure.y; |
|
|
|
event.configure.width = xevent.xconfigure.width; |
|
|
|
event.configure.height = xevent.xconfigure.height; |
|
|
|
break; |
|
|
|
case Expose: |
|
|
|
event.type = PUGL_EXPOSE; |
|
|
|
event.expose.x = xevent.xexpose.x; |
|
|
|
event.expose.y = xevent.xexpose.y; |
|
|
|
event.expose.width = xevent.xexpose.width; |
|
|
|
event.expose.height = xevent.xexpose.height; |
|
|
|
event.expose.count = xevent.xexpose.count; |
|
|
|
break; |
|
|
|
case MotionNotify: |
|
|
|
event.type = PUGL_MOTION_NOTIFY; |
|
|
|
event.motion.time = xevent.xmotion.time; |
|
|
|
event.motion.x = xevent.xmotion.x; |
|
|
|
event.motion.y = xevent.xmotion.y; |
|
|
|
event.motion.x_root = xevent.xmotion.x_root; |
|
|
|
event.motion.y_root = xevent.xmotion.y_root; |
|
|
|
event.motion.state = translateModifiers(xevent.xmotion.state); |
|
|
|
event.motion.is_hint = (xevent.xmotion.is_hint == NotifyHint); |
|
|
|
break; |
|
|
|
case ButtonPress: |
|
|
|
if (xevent.xbutton.button >= 4 && xevent.xbutton.button <= 7) { |
|
|
|
event.type = PUGL_SCROLL; |
|
|
|
event.scroll.time = xevent.xbutton.time; |
|
|
|
event.scroll.x = xevent.xbutton.x; |
|
|
|
event.scroll.y = xevent.xbutton.y; |
|
|
|
event.scroll.x_root = xevent.xbutton.x_root; |
|
|
|
event.scroll.y_root = xevent.xbutton.y_root; |
|
|
|
event.scroll.state = translateModifiers(xevent.xbutton.state); |
|
|
|
event.scroll.dx = 0.0; |
|
|
|
event.scroll.dy = 0.0; |
|
|
|
switch (xevent.xbutton.button) { |
|
|
|
case 4: event.scroll.dy = 1.0f; break; |
|
|
|
case 5: event.scroll.dy = -1.0f; break; |
|
|
|
case 6: event.scroll.dx = -1.0f; break; |
|
|
|
case 7: event.scroll.dx = 1.0f; break; |
|
|
|
} |
|
|
|
} |
|
|
|
// nobreak |
|
|
|
case ButtonRelease: |
|
|
|
if (xevent.xbutton.button < 4 || xevent.xbutton.button > 7) { |
|
|
|
event.button.type = ((xevent.type == ButtonPress) |
|
|
|
? PUGL_BUTTON_PRESS |
|
|
|
: PUGL_BUTTON_RELEASE); |
|
|
|
event.button.time = xevent.xbutton.time; |
|
|
|
event.button.x = xevent.xbutton.x; |
|
|
|
event.button.y = xevent.xbutton.y; |
|
|
|
event.button.x_root = xevent.xbutton.x_root; |
|
|
|
event.button.y_root = xevent.xbutton.y_root; |
|
|
|
event.button.state = translateModifiers(xevent.xbutton.state); |
|
|
|
event.button.button = xevent.xbutton.button; |
|
|
|
} |
|
|
|
break; |
|
|
|
case KeyPress: |
|
|
|
case KeyRelease: |
|
|
|
event.type = ((xevent.type == KeyPress) |
|
|
|
? PUGL_KEY_PRESS |
|
|
|
: PUGL_KEY_RELEASE); |
|
|
|
event.key.time = xevent.xbutton.time; |
|
|
|
event.key.x = xevent.xbutton.x; |
|
|
|
event.key.y = xevent.xbutton.y; |
|
|
|
event.key.x_root = xevent.xbutton.x_root; |
|
|
|
event.key.y_root = xevent.xbutton.y_root; |
|
|
|
event.key.state = translateModifiers(xevent.xbutton.state); |
|
|
|
translateKey(&xevent, &event); |
|
|
|
break; |
|
|
|
case EnterNotify: |
|
|
|
case LeaveNotify: |
|
|
|
event.type = ((xevent.type == EnterNotify) |
|
|
|
? PUGL_ENTER_NOTIFY |
|
|
|
: PUGL_LEAVE_NOTIFY); |
|
|
|
event.crossing.time = xevent.xcrossing.time; |
|
|
|
event.crossing.x = xevent.xcrossing.x; |
|
|
|
event.crossing.y = xevent.xcrossing.y; |
|
|
|
event.crossing.x_root = xevent.xcrossing.x_root; |
|
|
|
event.crossing.y_root = xevent.xcrossing.y_root; |
|
|
|
event.crossing.state = translateModifiers(xevent.xcrossing.state); |
|
|
|
event.crossing.mode = PUGL_CROSSING_NORMAL; |
|
|
|
if (xevent.xcrossing.mode == NotifyGrab) { |
|
|
|
event.crossing.mode = PUGL_CROSSING_GRAB; |
|
|
|
} else if (xevent.xcrossing.mode == NotifyUngrab) { |
|
|
|
event.crossing.mode = PUGL_CROSSING_UNGRAB; |
|
|
|
} |
|
|
|
break; |
|
|
|
default: |
|
|
|
break; |
|
|
|
} |
|
|
|
|
|
|
|
const PuglKey special = keySymToSpecial(sym); |
|
|
|
if (special && view->specialFunc) { |
|
|
|
view->specialFunc(view, press, special); |
|
|
|
} else if (!special && view->keyboardFunc) { |
|
|
|
view->keyboardFunc(view, press, str[0]); |
|
|
|
} |
|
|
|
return event; |
|
|
|
} |
|
|
|
|
|
|
|
void |
|
|
|
puglGrabFocus(PuglView* view) |
|
|
|
{ |
|
|
|
XSetInputFocus( |
|
|
|
view->impl->display, view->impl->win, RevertToPointerRoot, CurrentTime); |
|
|
|
} |
|
|
|
|
|
|
|
PuglStatus |
|
|
|
puglProcessEvents(PuglView* view) |
|
|
|
{ |
|
|
|
XEvent event; |
|
|
|
XEvent xevent; |
|
|
|
while (XPending(view->impl->display) > 0) { |
|
|
|
XNextEvent(view->impl->display, &event); |
|
|
|
switch (event.type) { |
|
|
|
case MapNotify: |
|
|
|
puglReshape(view, view->width, view->height); |
|
|
|
break; |
|
|
|
case ConfigureNotify: |
|
|
|
if ((event.xconfigure.width != view->width) || |
|
|
|
(event.xconfigure.height != view->height)) { |
|
|
|
puglReshape(view, |
|
|
|
event.xconfigure.width, |
|
|
|
event.xconfigure.height); |
|
|
|
} |
|
|
|
break; |
|
|
|
case Expose: |
|
|
|
if (event.xexpose.count != 0) { |
|
|
|
break; |
|
|
|
} |
|
|
|
puglDisplay(view); |
|
|
|
break; |
|
|
|
case MotionNotify: |
|
|
|
setModifiers(view, event.xmotion.state, event.xmotion.time); |
|
|
|
if (view->motionFunc) { |
|
|
|
view->motionFunc(view, event.xmotion.x, event.xmotion.y); |
|
|
|
} |
|
|
|
break; |
|
|
|
case ButtonPress: |
|
|
|
setModifiers(view, event.xbutton.state, event.xbutton.time); |
|
|
|
if (event.xbutton.button >= 4 && event.xbutton.button <= 7) { |
|
|
|
if (view->scrollFunc) { |
|
|
|
float dx = 0, dy = 0; |
|
|
|
switch (event.xbutton.button) { |
|
|
|
case 4: dy = 1.0f; break; |
|
|
|
case 5: dy = -1.0f; break; |
|
|
|
case 6: dx = -1.0f; break; |
|
|
|
case 7: dx = 1.0f; break; |
|
|
|
} |
|
|
|
view->scrollFunc(view, |
|
|
|
event.xbutton.x, event.xbutton.y, |
|
|
|
dx, dy); |
|
|
|
} |
|
|
|
break; |
|
|
|
} |
|
|
|
// nobreak |
|
|
|
case ButtonRelease: |
|
|
|
setModifiers(view, event.xbutton.state, event.xbutton.time); |
|
|
|
if (view->mouseFunc && |
|
|
|
(event.xbutton.button < 4 || event.xbutton.button > 7)) { |
|
|
|
view->mouseFunc(view, |
|
|
|
event.xbutton.button, event.type == ButtonPress, |
|
|
|
event.xbutton.x, event.xbutton.y); |
|
|
|
XNextEvent(view->impl->display, &xevent); |
|
|
|
bool ignore = false; |
|
|
|
if (xevent.type == ClientMessage) { |
|
|
|
// Handle close message |
|
|
|
char* type = XGetAtomName(view->impl->display, |
|
|
|
xevent.xclient.message_type); |
|
|
|
if (!strcmp(type, "WM_PROTOCOLS") && view->closeFunc) { |
|
|
|
view->closeFunc(view); |
|
|
|
} |
|
|
|
break; |
|
|
|
case KeyPress: |
|
|
|
setModifiers(view, event.xkey.state, event.xkey.time); |
|
|
|
dispatchKey(view, &event, true); |
|
|
|
break; |
|
|
|
case KeyRelease: |
|
|
|
setModifiers(view, event.xkey.state, event.xkey.time); |
|
|
|
XFree(type); |
|
|
|
continue; |
|
|
|
} else if (xevent.type == KeyRelease) { |
|
|
|
// Ignore key repeat if necessary |
|
|
|
if (view->ignoreKeyRepeat && |
|
|
|
XEventsQueued(view->impl->display, QueuedAfterReading)) { |
|
|
|
XEvent next; |
|
|
|
XPeekEvent(view->impl->display, &next); |
|
|
|
if (next.type == KeyPress && |
|
|
|
next.xkey.time == event.xkey.time && |
|
|
|
next.xkey.keycode == event.xkey.keycode) { |
|
|
|
XNextEvent(view->impl->display, &event); |
|
|
|
break; |
|
|
|
} |
|
|
|
} |
|
|
|
dispatchKey(view, &event, false); |
|
|
|
break; |
|
|
|
case ClientMessage: { |
|
|
|
char* type = XGetAtomName(view->impl->display, |
|
|
|
event.xclient.message_type); |
|
|
|
if (!strcmp(type, "WM_PROTOCOLS")) { |
|
|
|
if (view->closeFunc) { |
|
|
|
view->closeFunc(view); |
|
|
|
next.xkey.time == xevent.xkey.time && |
|
|
|
next.xkey.keycode == xevent.xkey.keycode) { |
|
|
|
XNextEvent(view->impl->display, &xevent); |
|
|
|
ignore = true; |
|
|
|
} |
|
|
|
} |
|
|
|
XFree(type); |
|
|
|
} break; |
|
|
|
#ifdef PUGL_GRAB_FOCUS |
|
|
|
case EnterNotify: |
|
|
|
XSetInputFocus(view->impl->display, |
|
|
|
view->impl->win, |
|
|
|
RevertToPointerRoot, |
|
|
|
CurrentTime); |
|
|
|
break; |
|
|
|
#endif |
|
|
|
default: |
|
|
|
break; |
|
|
|
} |
|
|
|
|
|
|
|
if (!ignore) { |
|
|
|
// Translate and dispatch event |
|
|
|
const PuglEvent event = translateEvent(view, xevent); |
|
|
|
puglDispatchEvent(view, &event); |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
if (view->redisplay) { |
|
|
|
puglDisplay(view); |
|
|
|
const PuglEventExpose expose = { |
|
|
|
PUGL_EXPOSE, view, true, 0.0, 0.0, (double)view->width, (double)view->height, 0 |
|
|
|
}; |
|
|
|
puglDispatchEvent(view, (const PuglEvent*)&expose); |
|
|
|
} |
|
|
|
|
|
|
|
return PUGL_SUCCESS; |
|
|
@@ -415,3 +496,17 @@ puglGetNativeWindow(PuglView* view) |
|
|
|
{ |
|
|
|
return view->impl->win; |
|
|
|
} |
|
|
|
|
|
|
|
void* |
|
|
|
puglGetContext(PuglView* view) |
|
|
|
{ |
|
|
|
#ifdef PUGL_HAVE_CAIRO |
|
|
|
if (view->ctx_type == PUGL_CAIRO) { |
|
|
|
return view->impl->cr; |
|
|
|
} |
|
|
|
#endif |
|
|
|
return NULL; |
|
|
|
|
|
|
|
// possibly unused |
|
|
|
(void)view; |
|
|
|
} |