Browse Source

Final pugl update

gh-pages
falkTX 10 years ago
parent
commit
2497744774
2 changed files with 299 additions and 203 deletions
  1. +1
    -0
      dgl/src/Window.cpp
  2. +298
    -203
      dgl/src/pugl/pugl_x11.c

+ 1
- 0
dgl/src/Window.cpp View File

@@ -190,6 +190,7 @@ struct Window::PrivateData {
puglSetCloseFunc(fView, onCloseCallback);

puglCreateWindow(fView, nullptr);
puglEnterContext(fView);

PuglInternals* impl = fView->impl;
#if defined(DISTRHO_OS_WINDOWS)


+ 298
- 203
dgl/src/pugl/pugl_x11.c View File

@@ -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;
}

Loading…
Cancel
Save