Nuklear/x11.c

339 lines
10 KiB
C

/*
Copyright (c) 2015
vurtun <polygone@gmx.net>
MIT licence
*/
#include <stdio.h>
#include <stdlib.h>
#include <stdarg.h>
#include <limits.h>
#include <errno.h>
#include <string.h>
#include <memory.h>
#include <assert.h>
#include <time.h>
#include <errno.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <sys/select.h>
#include <sys/time.h>
#include <unistd.h>
#include <fcntl.h>
#include <X11/Xlib.h>
#include <X11/Xutil.h>
#include <GL/gl.h>
#include <GL/glx.h>
#include <GL/glu.h>
#include "gui.h"
/* macros */
#define internal static
#define global static
#define persistent static
#define WIN_WIDTH 800
#define WIN_HEIGHT 600
#define DTIME 33
#define MAX_VERTEX_BUFFER (16 * 1024)
#define CONSOLE_MAX_CMDLINE 1024
#define CONSOLE_MAX_TEXT (16 * 1024)
#define MIN(a,b)((a) < (b) ? (a) : (b))
#define MAX(a,b)((a) < (b) ? (b) : (a))
#define CLAMP(i,v,x) (MAX(MIN(v,x), i))
#define LEN(a)(sizeof(a)/sizeof(a)[0])
#define UNUSED(a)((void)(a))
/* types */
struct XWindow {
Display *dpy;
Window root;
XVisualInfo *vi;
Colormap cmap;
XSetWindowAttributes swa;
XWindowAttributes gwa;
Window win;
GLXContext glc;
int running;
};
struct Console {
struct XWindow *win;
struct gui_draw_list gui;
struct gui_input input;
struct gui_font font;
/* text */
int output_len;
char output[CONSOLE_MAX_TEXT];
char line[CONSOLE_MAX_CMDLINE];
int line_len;
};
/* functions */
internal void die(const char*,...);
internal long timestamp(void);
internal void sleep_for(long ms);
internal void kpress(struct Console*, XEvent*);
internal void bpress(struct Console*, XEvent*);
internal void brelease(struct Console*, XEvent*);
internal void bmotion(struct Console*, XEvent*);
internal void resize(struct Console*, XEvent*);
internal void
die(const char *fmt, ...)
{
va_list ap;
va_start(ap, fmt);
vfprintf(stderr, fmt, ap);
va_end(ap);
exit(EXIT_FAILURE);
}
internal void*
xcalloc(size_t nmemb, size_t size)
{
void *p = calloc(nmemb, size);
if (!p)
die("out of memory\n");
return p;
}
internal long
timestamp(void)
{
struct timeval tv;
if (gettimeofday(&tv, NULL) < 0) return 0;
return (long)((long)tv.tv_sec * 1000 + (long)tv.tv_usec/1000);
}
internal void
sleep_for(long t)
{
const time_t sec = (int)(t/1000);
const long ms = t - (sec * 1000);
struct timespec req;
req.tv_sec = sec;
req.tv_nsec = ms * 1000000L;
while(-1 == nanosleep(&req, &req));
}
static void
kpress(struct Console *con, XEvent* e)
{
int ret;
struct XWindow *xw = con->win;
KeySym *keysym = XGetKeyboardMapping(xw->dpy, e->xkey.keycode, 1, &ret);
if (*keysym == XK_Escape) xw->running = 0;
else if (*keysym == XK_Shift_L || *keysym == XK_Shift_R) gui_input_key(&con->input, GUI_KEY_SHIFT, gui_true);
else if (*keysym == XK_Control_L || *keysym == XK_Control_L) gui_input_key(&con->input, GUI_KEY_CTRL, gui_true);
else if (*keysym == XK_Delete) gui_input_key(&con->input, GUI_KEY_DEL, gui_true);
else if (*keysym == XK_Return) gui_input_key(&con->input, GUI_KEY_ENTER, gui_true);
else if (*keysym == XK_BackSpace) gui_input_key(&con->input, GUI_KEY_BACKSPACE, gui_true);
}
static void
krelease(struct Console *con, XEvent* e)
{
int ret;
struct XWindow *xw = con->win;
KeySym *keysym = XGetKeyboardMapping(xw->dpy, e->xkey.keycode, 1, &ret);
if (*keysym == XK_Shift_L || *keysym == XK_Shift_R) gui_input_key(&con->input, GUI_KEY_SHIFT, gui_false);
else if (*keysym == XK_Control_L || *keysym == XK_Control_L) gui_input_key(&con->input, GUI_KEY_CTRL, gui_false);
else if (*keysym == XK_Delete) gui_input_key(&con->input, GUI_KEY_DEL, gui_false);
else if (*keysym == XK_Return) gui_input_key(&con->input, GUI_KEY_ENTER, gui_false);
else if (*keysym == XK_BackSpace) gui_input_key(&con->input, GUI_KEY_BACKSPACE, gui_false);
}
static void
bpress(struct Console *con, XEvent *evt)
{
const float x = evt->xbutton.x;
const float y = evt->xbutton.y;
if (evt->xbutton.button == Button1)
gui_input_button(&con->input, x, y, gui_true);
}
static void
brelease(struct Console *con, XEvent *evt)
{
const float x = evt->xbutton.x;
const float y = evt->xbutton.y;
struct XWindow *xw = con->win;
if (evt->xbutton.button == Button1)
gui_input_button(&con->input, x, y, gui_false);
}
static void
bmotion(struct Console *con, XEvent *evt)
{
const float x = evt->xbutton.x;
const float y = evt->xbutton.y;
struct XWindow *xw = con->win;
gui_input_motion(&con->input, x, y);
}
static void
resize(struct Console *con, XEvent* evt)
{
struct XWindow *xw = con->win;
XGetWindowAttributes(xw->dpy, xw->win, &xw->gwa);
glViewport(0, 0, xw->gwa.width, xw->gwa.height);
}
static void
draw(int width, int height, const struct gui_draw_list *list)
{
const struct gui_draw_command *cmd;
persistent const size_t v = sizeof(struct gui_vertex);
persistent const size_t p = offsetof(struct gui_vertex, pos);
persistent const size_t t = offsetof(struct gui_vertex, uv);
persistent const size_t c = offsetof(struct gui_vertex, color);
if (!list) return;
glPushAttrib(GL_ENABLE_BIT | GL_COLOR_BUFFER_BIT | GL_TRANSFORM_BIT);
glEnable(GL_BLEND);
glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
glDisable(GL_CULL_FACE);
glDisable(GL_DEPTH_TEST);
glEnable(GL_SCISSOR_TEST);
glEnableClientState(GL_VERTEX_ARRAY);
glEnableClientState(GL_TEXTURE_COORD_ARRAY);
glEnableClientState(GL_COLOR_ARRAY);
glEnable(GL_TEXTURE_2D);
glMatrixMode(GL_PROJECTION);
glPushMatrix();
glLoadIdentity();
glOrtho(0.0f, width, height, 0.0f, 0.0f, 1.0f);
glMatrixMode(GL_MODELVIEW);
glPushMatrix();
glLoadIdentity();
cmd = list->begin;
while (cmd) {
const int x = (int)cmd->clip_rect.x;
const int y = height - (int)(cmd->clip_rect.y + cmd->clip_rect.h);
const int w = (int)cmd->clip_rect.w;
const int h = (int)cmd->clip_rect.h;
gui_byte *buffer = (gui_byte*)cmd->vertexes;
glVertexPointer(2, GL_FLOAT, v, (void*)(buffer + p));
glTexCoordPointer(2, GL_FLOAT, v, (void*)(buffer + t));
glColorPointer(4, GL_UNSIGNED_BYTE, v, (void*)(buffer + c));
glBindTexture(GL_TEXTURE_2D, (unsigned long)cmd->texture);
glScissor(x, y, w, h);
glDrawArrays(GL_TRIANGLES, 0, cmd->vertex_count);
cmd = gui_next(list, cmd);
}
glDisableClientState(GL_COLOR_ARRAY);
glDisableClientState(GL_TEXTURE_COORD_ARRAY);
glDisableClientState(GL_VERTEX_ARRAY);
glMatrixMode(GL_MODELVIEW);
glPopMatrix();
glMatrixMode(GL_PROJECTION);
glPopMatrix();
glPopAttrib();
}
int
main(int argc, char *argv[])
{
struct XWindow xw;
struct Console con;
long dt, started;
gui_byte *buffer;
gui_size buffer_size = MAX_VERTEX_BUFFER;
const struct gui_color colorA = {100, 100, 100, 255};
const struct gui_color colorB = {45, 45, 45, 255};
const struct gui_color colorC = {0, 0, 0, 255};
static GLint att[] = {GLX_RGBA, GLX_DEPTH_SIZE,24, GLX_DOUBLEBUFFER, None};
gui_float slider = 5.0f;
gui_float prog = 60.0f;
gui_float offset = 300;
/* x11 */
UNUSED(argc); UNUSED(argv);
memset(&xw, 0, sizeof xw);
memset(&con, 0, sizeof con);
xw.dpy = XOpenDisplay(NULL);
if (!xw.dpy)
die("XOpenDisplay failed\n");
xw.root = DefaultRootWindow(xw.dpy);
xw.vi = glXChooseVisual(xw.dpy, 0, att);
if (!xw.vi)
die("Failed to find appropriate visual\n");
xw.cmap = XCreateColormap(xw.dpy,xw.root,xw.vi->visual,AllocNone);
xw.swa.colormap = xw.cmap;
xw.swa.event_mask =
ExposureMask | KeyPressMask | ButtonPress |
ButtonReleaseMask | ButtonMotionMask |
Button1MotionMask | Button2MotionMask | Button3MotionMask;
xw.win = XCreateWindow(
xw.dpy, xw.root, 0, 0,WIN_WIDTH,WIN_HEIGHT, 0,
xw.vi->depth, InputOutput, xw.vi->visual,
CWColormap | CWEventMask, &xw.swa);
XGetWindowAttributes(xw.dpy, xw.win, &xw.gwa);
XStoreName(xw.dpy, xw.win, "Demo");
XMapWindow(xw.dpy, xw.win);
XFlush(xw.dpy);
XSync(xw.dpy, False);
con.win = &xw;
/* OpenGL */
xw.glc = glXCreateContext(xw.dpy, xw.vi, NULL, GL_TRUE);
glXMakeCurrent(xw.dpy, xw.win, xw.glc);
buffer = xcalloc(MAX_VERTEX_BUFFER, 1);
xw.running = 1;
while (xw.running) {
/* Input */
XEvent ev;
started = timestamp();
gui_input_begin(&con.input);
while (XCheckWindowEvent(xw.dpy, xw.win, xw.swa.event_mask, &ev)) {
if (ev.type == Expose || ev.type == ConfigureNotify) resize(&con, &ev);
else if (ev.type == MotionNotify) bmotion(&con, &ev);
else if (ev.type == ButtonPress) bpress(&con, &ev);
else if (ev.type == ButtonRelease) brelease(&con, &ev);
else if (ev.type == KeyPress) kpress(&con, &ev);
else if (ev.type == KeyRelease) kpress(&con, &ev);
}
gui_input_end(&con.input);
/* ------------------------- GUI --------------------------*/
gui_begin(&con.gui, buffer, MAX_VERTEX_BUFFER);
if (gui_button(&con.gui, &con.input, colorA, colorC, 50,50,150,30,5,"",0))
fprintf(stdout, "Button pressed!\n");
slider = gui_slider(&con.gui, &con.input, colorA, colorB, 50,100,150,30,2, 0.0f, slider, 10.0f, 1.0f);
prog = gui_progress(&con.gui, &con.input, colorA, colorB, 50,150,150,30,2, prog, 100.0f, gui_true);
offset = gui_scroll(&con.gui, &con.input, colorA, colorB, 250,50,16,332, offset, 600);
gui_end(&con.gui);
/* ---------------------------------------------------------*/
/* Draw */
glClearColor(45.0f/255.0f,45.0f/255.0f,45.0f/255.0f,1);
glClear(GL_COLOR_BUFFER_BIT);
draw(xw.gwa.width, xw.gwa.height, &con.gui);
glXSwapBuffers(xw.dpy, xw.win);
/* Timing */
dt = timestamp() - started;
if (dt < DTIME) {
sleep_for(DTIME - dt);
dt = DTIME;
}
}
/* Cleanup */
glXMakeCurrent(xw.dpy, None, NULL);
glXDestroyContext(xw.dpy, xw.glc);
XDestroyWindow(xw.dpy, xw.win);
XCloseDisplay(xw.dpy);
return 0;
}