commit 7ce09b3b89a5c22e132fbeb19457f3cb3e71dc54
parent 49cd18b9bf49dee6c2c9d13bba52fe50f7998505
Author: Michael Savage <mikejsavage@gmail.com>
Date: Mon, 3 Sep 2018 15:12:15 +0300
Switch to C++, better text representation
Diffstat:
Makefile | | | 16 | +++++++++------- |
src/common.h | | | 50 | +++++++++++++++++++++++++------------------------- |
src/input.c | | | 210 | ------------------------------------------------------------------------------- |
src/input.cc | | | 210 | +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ |
src/main.c | | | 29 | ----------------------------- |
src/main.cc | | | 29 | +++++++++++++++++++++++++++++ |
src/script.c | | | 235 | ------------------------------------------------------------------------------- |
src/script.cc | | | 200 | +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ |
src/script.h | | | 4 | ++-- |
src/textbox.c | | | 209 | ------------------------------------------------------------------------------- |
src/textbox.cc | | | 125 | +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ |
src/textbox.h | | | 73 | +++++++++++++++++++++++++++++++++---------------------------------------- |
src/ui.c | | | 461 | ------------------------------------------------------------------------------- |
src/ui.cc | | | 401 | +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ |
14 files changed, 1034 insertions(+), 1218 deletions(-)
diff --git a/Makefile b/Makefile
@@ -1,13 +1,15 @@
-CC = gcc
SRCDIR = src
-CFLAGS = -O2 -std=gnu99 $(shell pkg-config --cflags lua5.1)
-LIBS = -lX11 -L/usr/X11R6/lib $(shell pkg-config --libs lua5.1)
-WARNINGS = -Wall -Wextra -Werror
+CXXFLAGS = -std=c++11 $(shell pkg-config --cflags lua51) -O0 -ggdb
+LIBS = -lm -lX11 -L/usr/X11R6/lib $(shell pkg-config --libs lua51)
+WARNINGS = -Wall -Wextra -Wno-unused-parameter -Wno-unused-function -Wshadow -Wcast-align -Wstrict-overflow -Wvla
OBJS = main.o textbox.o input.o ui.o script.o
.PHONY: all
all: ${OBJS}
- ${CC} -o mudGangster ${OBJS} ${CFLAGS} ${LIBS} ${WARNINGS}
+ ${CXX} -o mudGangster ${OBJS} ${CXXFLAGS} ${LIBS} ${WARNINGS}
-%.o: ${SRCDIR}/%.c
- ${CC} -c ${SRCDIR}/$*.c ${CFLAGS} ${LIBS} ${WARNINGS}
+%.o: ${SRCDIR}/%.cc
+ ${CXX} -c ${SRCDIR}/$*.cc ${CXXFLAGS} ${LIBS} ${WARNINGS}
+
+clean:
+ rm -f mudGangster ${OBJS}
diff --git a/src/common.h b/src/common.h
@@ -1,16 +1,12 @@
-#ifndef _COMMON_H_
-#define _COMMON_H_
-
-typedef enum { false, true } bool;
+#pragma once
#include <X11/Xlib.h>
#include "config.h"
#include "textbox.h"
-struct
-{
- Display* display;
+struct UIDefs {
+ Display * display;
int screen;
GC gc;
@@ -18,18 +14,19 @@ struct
Window window;
- TextBox* textMain;
- TextBox* textChat;
+ TextBox textMain;
+ TextBox textChat;
int width;
int height;
int depth;
int hasFocus;
-} UI;
+};
+
+extern UIDefs UI;
-typedef struct
-{
+struct MudFont {
int ascent;
int descent;
@@ -39,11 +36,10 @@ typedef struct
int height;
int width;
- XFontStruct* font;
-} MudFont;
+ XFontStruct * font;
+};
-struct
-{
+struct StyleDefs {
ulong bg;
ulong fg;
@@ -60,10 +56,8 @@ struct
MudFont font;
MudFont fontBold;
- union
- {
- struct
- {
+ union {
+ struct {
ulong black;
ulong red;
ulong green;
@@ -87,12 +81,18 @@ struct
ulong colours[ 2 ][ 8 ];
};
-} Style;
+};
-#endif // _COMMON_H_
+extern StyleDefs Style;
-#define PRETEND_TO_USE( x ) ( void ) ( x )
#define STRL( x ) ( x ), sizeof( x ) - 1
-#define MIN( a, b ) ( ( a ) < ( b ) ? ( a ) : ( b ) )
-#define MAX( a, b ) ( ( a ) > ( b ) ? ( a ) : ( b ) )
+template< typename T >
+constexpr T min( T a, T b ) {
+ return a < b ? a : b;
+}
+
+template< typename T >
+constexpr T max( T a, T b ) {
+ return a > b ? a : b;
+}
diff --git a/src/input.c b/src/input.c
@@ -1,210 +0,0 @@
-#include <stdlib.h>
-#include <string.h>
-
-#include "common.h"
-#include "input.h"
-#include "script.h"
-
-typedef struct
-{
- char* text;
- int len;
-} InputHistory;
-
-static InputHistory inputHistory[ MAX_INPUT_HISTORY ];
-static int inputHistoryHead = 0;
-static int inputHistoryCount = 0;
-static int inputHistoryDelta = 0;
-
-static char* inputBuffer = NULL;
-static char* starsBuffer = NULL;
-
-static int inputBufferSize = 256;
-
-static int inputLen = 0;
-static int inputPos = 0;
-
-void input_send()
-{
- if( inputLen > 0 )
- {
- InputHistory* lastCmd = &inputHistory[ ( inputHistoryHead + inputHistoryCount - 1 ) % MAX_INPUT_HISTORY ];
-
- if( inputLen != lastCmd->len || strncmp( inputBuffer, lastCmd->text, inputLen ) != 0 )
- {
- int pos = ( inputHistoryHead + inputHistoryCount ) % MAX_INPUT_HISTORY;
-
- if( inputHistoryCount == MAX_INPUT_HISTORY )
- {
- free( inputHistory[ pos ].text );
-
- inputHistoryHead = ( inputHistoryHead + 1 ) % MAX_INPUT_HISTORY;
- }
- else
- {
- inputHistoryCount++;
- }
-
- inputHistory[ pos ].text = malloc( inputLen );
-
- memcpy( inputHistory[ pos ].text, inputBuffer, inputLen );
- inputHistory[ pos ].len = inputLen;
- }
- }
-
- script_handleInput( inputBuffer, inputLen );
-
- inputHistoryDelta = 0;
-
- inputLen = 0;
- inputPos = 0;
-
- input_draw();
-}
-
-void input_backspace()
-{
- if( inputPos > 0 )
- {
- memmove( inputBuffer + inputPos - 1, inputBuffer + inputPos, inputLen - inputPos );
-
- inputLen--;
- inputPos--;
- }
-
- input_draw();
-}
-
-void input_delete()
-{
- if( inputPos < inputLen )
- {
- memmove( inputBuffer + inputPos, inputBuffer + inputPos + 1, inputLen - inputPos );
-
- inputLen--;
- }
-
- input_draw();
-}
-
-void input_up()
-{
- if( inputHistoryDelta >= inputHistoryCount )
- {
- return;
- }
-
- inputHistoryDelta++;
- int pos = ( inputHistoryHead + inputHistoryCount - inputHistoryDelta ) % MAX_INPUT_HISTORY;
-
- InputHistory cmd = inputHistory[ pos ];
-
- memcpy( inputBuffer, cmd.text, cmd.len );
-
- inputLen = cmd.len;
- inputPos = cmd.len;
-
- input_draw();
-}
-
-void input_down()
-{
- if( inputHistoryDelta == 0 )
- {
- return;
- }
-
- inputHistoryDelta--;
-
- if( inputHistoryDelta != 0 )
- {
- int pos = ( inputHistoryHead + inputHistoryCount - inputHistoryDelta ) % MAX_INPUT_HISTORY;
-
- InputHistory cmd = inputHistory[ pos ];
-
- memcpy( inputBuffer, cmd.text, cmd.len );
-
- inputLen = cmd.len;
- inputPos = cmd.len;
- }
- else
- {
- inputLen = 0;
- inputPos = 0;
- }
-
- input_draw();
-}
-
-void input_left()
-{
- inputPos = MAX( inputPos - 1, 0 );
-
- input_draw();
-}
-
-void input_right()
-{
- inputPos = MIN( inputPos + 1, inputLen );
-
- input_draw();
-}
-
-void input_add( char* buffer, int len )
-{
- if( inputLen + len >= inputBufferSize )
- {
- inputBufferSize *= 2;
-
- inputBuffer = realloc( inputBuffer, inputBufferSize );
- starsBuffer = realloc( starsBuffer, inputBufferSize );
-
- memset( starsBuffer + inputBufferSize / 2, '*', inputBufferSize / 2 );
- }
-
- if( inputPos < inputLen )
- {
- memmove( inputBuffer + inputPos + len, inputBuffer + inputPos, inputLen - inputPos );
- }
-
- memcpy( inputBuffer + inputPos, buffer, len );
-
- inputLen += len;
- inputPos += len;
-
- input_draw();
-}
-
-void input_draw()
-{
- XSetFont( UI.display, UI.gc, Style.font.font->fid );
-
- XSetForeground( UI.display, UI.gc, Style.bg );
- XFillRectangle( UI.display, UI.window, UI.gc, PADDING, UI.height - ( PADDING + Style.font.height ), UI.width - 6, Style.font.height );
-
- XSetForeground( UI.display, UI.gc, Style.fg );
- XDrawString( UI.display, UI.window, UI.gc, PADDING, UI.height - ( PADDING + Style.font.descent ), inputBuffer, inputLen );
-
- XSetForeground( UI.display, UI.gc, Style.cursor );
- XFillRectangle( UI.display, UI.window, UI.gc, PADDING + Style.font.width * inputPos, UI.height - ( PADDING + Style.font.height ), Style.font.width, Style.font.height );
-
- if( inputPos < inputLen )
- {
- XSetForeground( UI.display, UI.gc, Style.bg );
- XDrawString( UI.display, UI.window, UI.gc, PADDING + Style.font.width * inputPos, UI.height - ( PADDING + Style.font.descent ), inputBuffer + inputPos, 1 );
- }
-}
-
-void input_init()
-{
- inputBuffer = malloc( inputBufferSize );
- starsBuffer = malloc( inputBufferSize );
-
- memset( starsBuffer, '*', inputBufferSize );
-}
-
-void input_end()
-{
- free( inputBuffer );
- free( starsBuffer );
-}
diff --git a/src/input.cc b/src/input.cc
@@ -0,0 +1,210 @@
+#include <stdlib.h>
+#include <string.h>
+
+#include "common.h"
+#include "input.h"
+#include "script.h"
+
+typedef struct
+{
+ char* text;
+ int len;
+} InputHistory;
+
+static InputHistory inputHistory[ MAX_INPUT_HISTORY ];
+static int inputHistoryHead = 0;
+static int inputHistoryCount = 0;
+static int inputHistoryDelta = 0;
+
+static char* inputBuffer = NULL;
+static char* starsBuffer = NULL;
+
+static int inputBufferSize = 256;
+
+static int inputLen = 0;
+static int inputPos = 0;
+
+void input_send()
+{
+ if( inputLen > 0 )
+ {
+ InputHistory* lastCmd = &inputHistory[ ( inputHistoryHead + inputHistoryCount - 1 ) % MAX_INPUT_HISTORY ];
+
+ if( inputLen != lastCmd->len || strncmp( inputBuffer, lastCmd->text, inputLen ) != 0 )
+ {
+ int pos = ( inputHistoryHead + inputHistoryCount ) % MAX_INPUT_HISTORY;
+
+ if( inputHistoryCount == MAX_INPUT_HISTORY )
+ {
+ free( inputHistory[ pos ].text );
+
+ inputHistoryHead = ( inputHistoryHead + 1 ) % MAX_INPUT_HISTORY;
+ }
+ else
+ {
+ inputHistoryCount++;
+ }
+
+ inputHistory[ pos ].text = ( char * ) malloc( inputLen );
+
+ memcpy( inputHistory[ pos ].text, inputBuffer, inputLen );
+ inputHistory[ pos ].len = inputLen;
+ }
+ }
+
+ script_handleInput( inputBuffer, inputLen );
+
+ inputHistoryDelta = 0;
+
+ inputLen = 0;
+ inputPos = 0;
+
+ input_draw();
+}
+
+void input_backspace()
+{
+ if( inputPos > 0 )
+ {
+ memmove( inputBuffer + inputPos - 1, inputBuffer + inputPos, inputLen - inputPos );
+
+ inputLen--;
+ inputPos--;
+ }
+
+ input_draw();
+}
+
+void input_delete()
+{
+ if( inputPos < inputLen )
+ {
+ memmove( inputBuffer + inputPos, inputBuffer + inputPos + 1, inputLen - inputPos );
+
+ inputLen--;
+ }
+
+ input_draw();
+}
+
+void input_up()
+{
+ if( inputHistoryDelta >= inputHistoryCount )
+ {
+ return;
+ }
+
+ inputHistoryDelta++;
+ int pos = ( inputHistoryHead + inputHistoryCount - inputHistoryDelta ) % MAX_INPUT_HISTORY;
+
+ InputHistory cmd = inputHistory[ pos ];
+
+ memcpy( inputBuffer, cmd.text, cmd.len );
+
+ inputLen = cmd.len;
+ inputPos = cmd.len;
+
+ input_draw();
+}
+
+void input_down()
+{
+ if( inputHistoryDelta == 0 )
+ {
+ return;
+ }
+
+ inputHistoryDelta--;
+
+ if( inputHistoryDelta != 0 )
+ {
+ int pos = ( inputHistoryHead + inputHistoryCount - inputHistoryDelta ) % MAX_INPUT_HISTORY;
+
+ InputHistory cmd = inputHistory[ pos ];
+
+ memcpy( inputBuffer, cmd.text, cmd.len );
+
+ inputLen = cmd.len;
+ inputPos = cmd.len;
+ }
+ else
+ {
+ inputLen = 0;
+ inputPos = 0;
+ }
+
+ input_draw();
+}
+
+void input_left()
+{
+ inputPos = max( inputPos - 1, 0 );
+
+ input_draw();
+}
+
+void input_right()
+{
+ inputPos = min( inputPos + 1, inputLen );
+
+ input_draw();
+}
+
+void input_add( char* buffer, int len )
+{
+ if( inputLen + len >= inputBufferSize )
+ {
+ inputBufferSize *= 2;
+
+ inputBuffer = ( char * ) realloc( inputBuffer, inputBufferSize );
+ starsBuffer = ( char * ) realloc( starsBuffer, inputBufferSize );
+
+ memset( starsBuffer + inputBufferSize / 2, '*', inputBufferSize / 2 );
+ }
+
+ if( inputPos < inputLen )
+ {
+ memmove( inputBuffer + inputPos + len, inputBuffer + inputPos, inputLen - inputPos );
+ }
+
+ memcpy( inputBuffer + inputPos, buffer, len );
+
+ inputLen += len;
+ inputPos += len;
+
+ input_draw();
+}
+
+void input_draw()
+{
+ XSetFont( UI.display, UI.gc, Style.font.font->fid );
+
+ XSetForeground( UI.display, UI.gc, Style.bg );
+ XFillRectangle( UI.display, UI.window, UI.gc, PADDING, UI.height - ( PADDING + Style.font.height ), UI.width - 6, Style.font.height );
+
+ XSetForeground( UI.display, UI.gc, Style.fg );
+ XDrawString( UI.display, UI.window, UI.gc, PADDING, UI.height - ( PADDING + Style.font.descent ), inputBuffer, inputLen );
+
+ XSetForeground( UI.display, UI.gc, Style.cursor );
+ XFillRectangle( UI.display, UI.window, UI.gc, PADDING + Style.font.width * inputPos, UI.height - ( PADDING + Style.font.height ), Style.font.width, Style.font.height );
+
+ if( inputPos < inputLen )
+ {
+ XSetForeground( UI.display, UI.gc, Style.bg );
+ XDrawString( UI.display, UI.window, UI.gc, PADDING + Style.font.width * inputPos, UI.height - ( PADDING + Style.font.descent ), inputBuffer + inputPos, 1 );
+ }
+}
+
+void input_init()
+{
+ inputBuffer = ( char * ) malloc( inputBufferSize );
+ starsBuffer = ( char * ) malloc( inputBufferSize );
+
+ memset( starsBuffer, '*', inputBufferSize );
+}
+
+void input_end()
+{
+ free( inputBuffer );
+ free( starsBuffer );
+}
diff --git a/src/main.c b/src/main.c
@@ -1,29 +0,0 @@
-#include <stdio.h>
-#include <stdlib.h>
-#include <unistd.h>
-#include <string.h>
-
-#include <X11/Xutil.h>
-#include <X11/XKBlib.h>
-#include <X11/cursorfont.h>
-#include <X11/keysym.h>
-
-#include "common.h"
-#include "script.h"
-#include "input.h"
-#include "ui.h"
-
-int main()
-{
- ui_init();
- input_init();
- script_init();
-
- // main loop is done in lua
-
- script_end();
- input_end();
- ui_end();
-
- return EXIT_SUCCESS;
-}
diff --git a/src/main.cc b/src/main.cc
@@ -0,0 +1,29 @@
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <string.h>
+
+#include <X11/Xutil.h>
+#include <X11/XKBlib.h>
+#include <X11/cursorfont.h>
+#include <X11/keysym.h>
+
+#include "common.h"
+#include "script.h"
+#include "input.h"
+#include "ui.h"
+
+int main()
+{
+ ui_init();
+ input_init();
+ script_init();
+
+ // main loop is done in lua
+
+ script_end();
+ input_end();
+ ui_end();
+
+ return EXIT_SUCCESS;
+}
diff --git a/src/script.c b/src/script.c
@@ -1,235 +0,0 @@
-#include <stdlib.h>
-#include <assert.h>
-
-#include <lua.h>
-#include <lualib.h>
-#include <lauxlib.h>
-
-#include <X11/Xutil.h>
-
-#include "common.h"
-#include "ui.h"
-
-static lua_State* L;
-
-static int inputHandlerIdx = LUA_NOREF;
-static int macroHandlerIdx = LUA_NOREF;
-static int closeHandlerIdx = LUA_NOREF;
-
-void script_handleInput( char* buffer, int len )
-{
- assert( inputHandlerIdx != LUA_NOREF );
-
- lua_rawgeti( L, LUA_REGISTRYINDEX, inputHandlerIdx );
- lua_pushlstring( L, buffer, len );
-
- lua_call( L, 1, 0 );
-}
-
-void script_doMacro( char* key, int len, bool shift, bool ctrl, bool alt )
-{
- lua_rawgeti( L, LUA_REGISTRYINDEX, macroHandlerIdx );
-
- lua_pushlstring( L, key, len );
-
- lua_pushboolean( L, shift );
- lua_pushboolean( L, ctrl );
- lua_pushboolean( L, alt );
-
- lua_call( L, 4, 0 );
-}
-
-void script_handleClose()
-{
- assert( closeHandlerIdx != LUA_NOREF );
-
- lua_rawgeti( L, LUA_REGISTRYINDEX, closeHandlerIdx );
-
- lua_call( L, 0, 0 );
-}
-
-static int mud_handleXEvents( lua_State* L )
-{
- PRETEND_TO_USE( L );
-
- ui_handleXEvents();
-
- return 0;
-}
-
-static int mud_printMain( lua_State* L )
-{
- const char* str = luaL_checkstring( L, 1 );
- size_t len = lua_objlen( L, 1 );
-
- Colour fg = luaL_checkint( L, 2 );
- Colour bg = luaL_checkint( L, 3 );
- bool bold = lua_toboolean( L, 4 );
-
- textbox_add( UI.textMain, str, len, fg, bg, bold );
-
- return 0;
-}
-
-static int mud_newlineMain( lua_State* L )
-{
- PRETEND_TO_USE( L );
-
- textbox_newline( UI.textMain );
-
- return 0;
-}
-
-static int mud_drawMain( lua_State* L )
-{
- PRETEND_TO_USE( L );
-
- textbox_draw( UI.textMain );
-
- return 0;
-}
-
-static int mud_printChat( lua_State* L )
-{
- const char* str = luaL_checkstring( L, 1 );
- size_t len = lua_objlen( L, 1 );
-
- Colour fg = luaL_checkint( L, 2 );
- Colour bg = luaL_checkint( L, 3 );
- bool bold = lua_toboolean( L, 4 );
-
- textbox_add( UI.textChat, str, len, fg, bg, bold );
-
- return 0;
-}
-
-static int mud_newlineChat( lua_State* L )
-{
- PRETEND_TO_USE( L );
-
- textbox_newline( UI.textChat );
-
- return 0;
-}
-
-static int mud_drawChat( lua_State* L )
-{
- PRETEND_TO_USE( L );
-
- textbox_draw( UI.textChat );
-
- return 0;
-}
-
-static int mud_setStatus( lua_State* L )
-{
- luaL_argcheck( L, lua_type( L, 1 ) == LUA_TTABLE, 1, "expected function" );
-
- size_t len = lua_objlen( L, 1 );
-
- ui_statusClear();
-
- for( size_t i = 0; i < len; i++ )
- {
- lua_pushnumber( L, i + 1 );
- lua_gettable( L, 1 );
-
- lua_pushliteral( L, "text" );
- lua_gettable( L, 2 );
- size_t seglen;
- const char* str = lua_tolstring( L, -1, &seglen );
-
- lua_pushliteral( L, "fg" );
- lua_gettable( L, 2 );
- const Colour fg = lua_tointeger( L, -1 );
-
- lua_pushliteral( L, "fg" );
- lua_gettable( L, 2 );
- const bool bold = lua_toboolean( L, -1 );
-
- for( size_t j = 0; j < seglen; j++ ) {
- ui_statusAdd( str[ j ], fg, bold );
- }
-
- lua_pop( L, 4 );
- }
-
- ui_statusDraw();
-
- return 0;
-}
-
-static int mud_setHandlers( lua_State* L )
-{
- luaL_argcheck( L, lua_type( L, 1 ) == LUA_TFUNCTION, 1, "expected function" );
- luaL_argcheck( L, lua_type( L, 2 ) == LUA_TFUNCTION, 2, "expected function" );
- luaL_argcheck( L, lua_type( L, 3 ) == LUA_TFUNCTION, 3, "expected function" );
-
- closeHandlerIdx = luaL_ref( L, LUA_REGISTRYINDEX );
- macroHandlerIdx = luaL_ref( L, LUA_REGISTRYINDEX );
- inputHandlerIdx = luaL_ref( L, LUA_REGISTRYINDEX );
-
- return 0;
-}
-
-static int mud_urgent( lua_State* L )
-{
- PRETEND_TO_USE( L );
-
- if( !UI.hasFocus ) {
- XWMHints* hints = XGetWMHints( UI.display, UI.window );
- hints->flags |= XUrgencyHint;
- XSetWMHints( UI.display, UI.window, hints );
- XFree( hints );
- }
-
- return 0;
-}
-
-void script_init()
-{
- mud_handleXEvents( NULL );
-
- L = lua_open();
- luaL_openlibs( L );
-
- lua_getglobal( L, "debug" );
- lua_getfield( L, -1, "traceback" );
- lua_remove( L, -2 );
-
- if( luaL_loadfile( L, "main.lua" ) )
- {
- printf( "Error reading main.lua: %s\n", lua_tostring( L, -1 ) );
-
- exit( 1 );
- }
-
- lua_pushinteger( L, ConnectionNumber( UI.display ) );
- lua_pushcfunction( L, mud_handleXEvents );
-
- lua_pushcfunction( L, mud_printMain );
- lua_pushcfunction( L, mud_newlineMain );
- lua_pushcfunction( L, mud_drawMain );
-
- lua_pushcfunction( L, mud_printChat );
- lua_pushcfunction( L, mud_newlineChat );
- lua_pushcfunction( L, mud_drawChat );
-
- lua_pushcfunction( L, mud_setHandlers );
-
- lua_pushcfunction( L, mud_urgent );
-
- lua_pushcfunction( L, mud_setStatus );
-
- if( lua_pcall( L, 11, 0, -13 ) )
- {
- printf( "Error running main.lua: %s\n", lua_tostring( L, -1 ) );
-
- exit( 1 );
- }
-}
-
-void script_end()
-{
- lua_close( L );
-}
diff --git a/src/script.cc b/src/script.cc
@@ -0,0 +1,200 @@
+#include <stdlib.h>
+#include <assert.h>
+
+#include <lua.hpp>
+
+#include <X11/Xutil.h>
+
+#include "common.h"
+#include "ui.h"
+
+static lua_State * lua;
+
+static int inputHandlerIdx = LUA_NOREF;
+static int macroHandlerIdx = LUA_NOREF;
+static int closeHandlerIdx = LUA_NOREF;
+
+void script_handleInput( const char * buffer, int len ) {
+ assert( inputHandlerIdx != LUA_NOREF );
+
+ lua_rawgeti( lua, LUA_REGISTRYINDEX, inputHandlerIdx );
+ lua_pushlstring( lua, buffer, len );
+
+ lua_call( lua, 1, 0 );
+}
+
+void script_doMacro( const char * key, int len, bool shift, bool ctrl, bool alt ) {
+ lua_rawgeti( lua, LUA_REGISTRYINDEX, macroHandlerIdx );
+
+ lua_pushlstring( lua, key, len );
+
+ lua_pushboolean( lua, shift );
+ lua_pushboolean( lua, ctrl );
+ lua_pushboolean( lua, alt );
+
+ lua_call( lua, 4, 0 );
+}
+
+void script_handleClose() {
+ assert( closeHandlerIdx != LUA_NOREF );
+
+ lua_rawgeti( lua, LUA_REGISTRYINDEX, closeHandlerIdx );
+
+ lua_call( lua, 0, 0 );
+}
+
+// namespace {
+
+extern "C" int mud_handleXEvents( lua_State * ) {
+ ui_handleXEvents();
+ return 0;
+}
+
+static void generic_print( TextBox * tb, lua_State * L ) {
+ const char* str = luaL_checkstring( L, 1 );
+ size_t len = lua_objlen( L, 1 );
+
+ Colour fg = Colour( luaL_checkinteger( L, 2 ) );
+ Colour bg = Colour( luaL_checkinteger( L, 3 ) );
+ bool bold = lua_toboolean( L, 4 );
+
+ textbox_add( tb, str, len, fg, bg, bold );
+}
+
+extern "C" int mud_printMain( lua_State * L ) {
+ generic_print( &UI.textMain, L );
+ return 0;
+}
+
+extern "C" int mud_newlineMain( lua_State * L ) {
+ textbox_newline( &UI.textMain );
+ return 0;
+}
+
+extern "C" int mud_drawMain( lua_State * L ) {
+ textbox_draw( &UI.textMain );
+ return 0;
+}
+
+extern "C" int mud_printChat( lua_State * L ) {
+ generic_print( &UI.textChat, L );
+ return 0;
+}
+
+extern "C" int mud_newlineChat( lua_State * L ) {
+ textbox_newline( &UI.textChat );
+ return 0;
+}
+
+extern "C" int mud_drawChat( lua_State * L ) {
+ textbox_draw( &UI.textChat );
+ return 0;
+}
+
+extern "C" int mud_setStatus( lua_State * L ) {
+ luaL_argcheck( L, lua_type( L, 1 ) == LUA_TTABLE, 1, "expected function" );
+
+ size_t len = lua_objlen( L, 1 );
+
+ ui_statusClear();
+
+ for( size_t i = 0; i < len; i++ )
+ {
+ lua_pushnumber( L, i + 1 );
+ lua_gettable( L, 1 );
+
+ lua_pushliteral( L, "text" );
+ lua_gettable( L, 2 );
+ size_t seglen;
+ const char* str = lua_tolstring( L, -1, &seglen );
+
+ lua_pushliteral( L, "fg" );
+ lua_gettable( L, 2 );
+ const Colour fg = Colour( lua_tointeger( L, -1 ) );
+
+ lua_pushliteral( L, "fg" );
+ lua_gettable( L, 2 );
+ const bool bold = lua_toboolean( L, -1 );
+
+ for( size_t j = 0; j < seglen; j++ ) {
+ ui_statusAdd( str[ j ], fg, bold );
+ }
+
+ lua_pop( L, 4 );
+ }
+
+ ui_statusDraw();
+
+ return 0;
+}
+
+extern "C" int mud_setHandlers( lua_State * L ) {
+ luaL_argcheck( L, lua_type( L, 1 ) == LUA_TFUNCTION, 1, "expected function" );
+ luaL_argcheck( L, lua_type( L, 2 ) == LUA_TFUNCTION, 2, "expected function" );
+ luaL_argcheck( L, lua_type( L, 3 ) == LUA_TFUNCTION, 3, "expected function" );
+
+ closeHandlerIdx = luaL_ref( L, LUA_REGISTRYINDEX );
+ macroHandlerIdx = luaL_ref( L, LUA_REGISTRYINDEX );
+ inputHandlerIdx = luaL_ref( L, LUA_REGISTRYINDEX );
+
+ return 0;
+}
+
+extern "C" int mud_urgent( lua_State * L ) {
+ if( !UI.hasFocus ) {
+ XWMHints* hints = XGetWMHints( UI.display, UI.window );
+ hints->flags |= XUrgencyHint;
+ XSetWMHints( UI.display, UI.window, hints );
+ XFree( hints );
+ }
+
+ return 0;
+}
+
+// } // anon namespace
+
+void script_init() {
+ mud_handleXEvents( NULL ); // TODO: why is this here?
+
+ lua = lua_open();
+ luaL_openlibs( lua );
+
+ lua_getglobal( lua, "debug" );
+ lua_getfield( lua, -1, "traceback" );
+ lua_remove( lua, -2 );
+
+ if( luaL_loadfile( lua, "main.lua" ) )
+ {
+ printf( "Error reading main.lua: %s\n", lua_tostring( lua, -1 ) );
+
+ exit( 1 );
+ }
+
+ lua_pushinteger( lua, ConnectionNumber( UI.display ) );
+ lua_pushcfunction( lua, mud_handleXEvents );
+
+ lua_pushcfunction( lua, mud_printMain );
+ lua_pushcfunction( lua, mud_newlineMain );
+ lua_pushcfunction( lua, mud_drawMain );
+
+ lua_pushcfunction( lua, mud_printChat );
+ lua_pushcfunction( lua, mud_newlineChat );
+ lua_pushcfunction( lua, mud_drawChat );
+
+ lua_pushcfunction( lua, mud_setHandlers );
+
+ lua_pushcfunction( lua, mud_urgent );
+
+ lua_pushcfunction( lua, mud_setStatus );
+
+ if( lua_pcall( lua, 11, 0, -13 ) )
+ {
+ printf( "Error running main.lua: %s\n", lua_tostring( lua, -1 ) );
+
+ exit( 1 );
+ }
+}
+
+void script_end() {
+ lua_close( lua );
+}
diff --git a/src/script.h b/src/script.h
@@ -1,8 +1,8 @@
#ifndef _SCRIPT_H_
#define _SCRIPT_H_
-void script_handleInput( char* buffer, int len );
-void script_doMacro( char* key, int len, bool shift, bool ctrl, bool alt );
+void script_handleInput( const char * buffer, int len );
+void script_doMacro( const char * key, int len, bool shift, bool ctrl, bool alt );
void script_handleClose();
void script_init();
diff --git a/src/textbox.c b/src/textbox.c
@@ -1,209 +0,0 @@
-#include <stdlib.h>
-#include <string.h>
-#include <assert.h>
-
-#include <X11/Xlib.h>
-
-#include "common.h"
-#include <stdio.h>
-
-TextBox* textbox_new( unsigned int maxLines )
-{
- TextBox* textbox = calloc( sizeof( TextBox ), 1 );
-
- assert( textbox != NULL );
-
- textbox->lines = malloc( maxLines * sizeof( Line ) );
- textbox->numLines = -1;
- textbox->maxLines = maxLines;
- textbox->scrollDelta = 0;
-
- Line* line = &textbox->lines[ 0 ];
-
- line->head = line->tail = calloc( sizeof( Text ), 1 );
-
- return textbox;
-}
-
-void textbox_freeline( TextBox* self, int idx )
-{
- Text* node = self->lines[ idx ].head;
-
- while( node != NULL )
- {
- Text* next = node->next;
-
- free( node->buffer );
- free( node );
-
- node = next;
- }
-}
-
-void textbox_free( TextBox* self )
-{
- for( int i = 0; i < self->numLines; i++ )
- {
- textbox_freeline( self, i );
- }
-
- free( self );
-}
-
-void textbox_setpos( TextBox* self, int x, int y )
-{
- self->x = x;
- self->y = y;
-}
-
-void textbox_setsize( TextBox* self, int width, int height )
-{
- self->width = width;
- self->height = height;
-
- self->rows = height / ( Style.font.height + SPACING );
- self->cols = width / Style.font.width;
-}
-
-void textbox_add( TextBox* self, const char* str, unsigned int len, Colour fg, Colour bg, bool bold )
-{
- if( self->numLines == -1 )
- {
- self->numLines = 0;
- }
-
- Line* line = &self->lines[ ( self->head + self->numLines ) % self->maxLines ];
-
- Text* node = malloc( sizeof( Text ) );
- node->buffer = malloc( len );
-
- memcpy( node->buffer, str, len );
- node->len = len;
-
- node->fg = fg;
- node->bg = bg;
- node->bold = bold;
-
- node->next = NULL;
-
- line->tail->next = node;
- line->tail = node;
-
- line->len += len;
-}
-
-void textbox_newline( TextBox* self )
-{
- if( self->numLines < self->maxLines )
- {
- self->numLines++;
-
- Line* line = &self->lines[ ( self->head + self->numLines ) % self->maxLines ];
-
- line->head = line->tail = calloc( sizeof( Text ), 1 );
-
- if( self->scrollDelta != 0 )
- {
- self->scrollDelta++;
- }
- }
- else
- {
- textbox_freeline( self, self->head );
-
- Line* line = &self->lines[ self->head ];
-
- line->head = line->tail = calloc( sizeof( Text ), 1 );
-
- self->head = ( self->head + 1 ) % self->maxLines;
- }
-}
-
-void textbox_draw( TextBox* self )
-{
- if( self->width == 0 || self->height == 0 ) {
- return;
- }
-
- Pixmap doublebuf = XCreatePixmap( UI.display, UI.window, self->width, self->height, UI.depth );
-
- XSetForeground( UI.display, UI.gc, Style.bg );
- XFillRectangle( UI.display, doublebuf, UI.gc, 0, 0, self->width, self->height );
-
- int rowsRemaining = self->rows;
- int linesRemaining = self->numLines - self->scrollDelta + 1;
-
- int lineTop = self->height;
- int linesPrinted = 0;
-
- int firstLine = self->head + self->numLines - self->scrollDelta;
-
- while( rowsRemaining > 0 && linesRemaining > 0 )
- {
- Line* line = &self->lines[ ( firstLine - linesPrinted ) % self->maxLines ];
-
- int lineHeight = ( line->len / self->cols );
-
- if( line->len % self->cols != 0 || line->len == 0 )
- {
- lineHeight++;
- }
-
- lineTop -= lineHeight * ( Style.font.height + SPACING );
-
- Text* node = line->head->next;
- int row = 0;
- int col = 0;
-
- while( node != NULL )
- {
- if( node->len != 0 )
- {
- XSetFont( UI.display, UI.gc, ( node->bold ? Style.fontBold : Style.font ).font->fid );
- XSetForeground( UI.display, UI.gc,
- node->fg == SYSTEM ? Style.Colours.system : Style.colours[ node->bold ][ node->fg ] );
-
- // TODO: draw bg
-
- int remainingChars = node->len;
- int pos = 0;
-
- while( remainingChars > 0 )
- {
- int charsToPrint = MIN( remainingChars, self->cols - col );
-
- int x = ( Style.font.width * col );
- int y = lineTop + ( ( Style.font.height + SPACING ) * row );
-
- if( y >= 0 )
- {
- XDrawString( UI.display, doublebuf, UI.gc, x, y + Style.font.ascent + SPACING, node->buffer + pos, charsToPrint );
- }
-
- pos += charsToPrint;
- remainingChars -= charsToPrint;
-
- if( remainingChars != 0 )
- {
- col = 0;
- row++;
- }
- else
- {
- col += charsToPrint;
- }
- }
- }
-
- node = node->next;
- }
-
- rowsRemaining -= lineHeight;
- linesRemaining--;
-
- linesPrinted++;
- }
-
- XCopyArea( UI.display, doublebuf, UI.window, UI.gc, 0, 0, self->width, self->height, self->x, self->y );
- XFreePixmap( UI.display, doublebuf );
-}
diff --git a/src/textbox.cc b/src/textbox.cc
@@ -0,0 +1,125 @@
+#include <stdlib.h>
+#include <string.h>
+#include <assert.h>
+
+#include <X11/Xlib.h>
+
+#include "common.h"
+#include <stdio.h>
+
+void textbox_init( TextBox * tb ) {
+ *tb = { };
+ // TODO: this is kinda crap
+ tb->text.lines = ( Line * ) calloc( sizeof( Line ), SCROLLBACK_SIZE );
+}
+
+void textbox_term( TextBox * tb ) {
+ free( tb->text.lines );
+}
+
+void textbox_setpos( TextBox * tb, int x, int y ) {
+ tb->x = x;
+ tb->y = y;
+}
+
+void textbox_setsize( TextBox * tb, int width, int height ) {
+ tb->width = width;
+ tb->height = height;
+}
+
+void textbox_add( TextBox * tb, const char * str, unsigned int len, Colour fg, Colour bg, bool bold ) {
+ Line * line = &tb->text.lines[ ( tb->text.head + tb->text.num_lines ) % SCROLLBACK_SIZE ];
+ size_t remaining = MAX_LINE_LENGTH - line->len;
+ size_t n = min( strlen( str ), remaining );
+
+ for( size_t i = 0; i < n; i++ ) {
+ Glyph & glyph = line->glyphs[ line->len + i ];
+ glyph.ch = str[ i ];
+ glyph.fg = fg;
+ glyph.bg = bg;
+ glyph.bold = bold;
+ };
+
+ line->len += n;
+}
+
+void textbox_newline( TextBox * tb ) {
+ if( tb->text.num_lines < SCROLLBACK_SIZE ) {
+ tb->text.num_lines++;
+ return;
+ }
+
+ tb->text.head++;
+ Line * line = &tb->text.lines[ ( tb->text.head + tb->text.num_lines ) % SCROLLBACK_SIZE ];
+ line->len = 0;
+}
+
+void textbox_draw( const TextBox * tb ) {
+ if( tb->width == 0 || tb->height == 0 ) {
+ return;
+ }
+
+ Pixmap doublebuf = XCreatePixmap( UI.display, UI.window, tb->width, tb->height, UI.depth );
+
+ XSetForeground( UI.display, UI.gc, Style.bg );
+ XFillRectangle( UI.display, doublebuf, UI.gc, 0, 0, tb->width, tb->height );
+
+ size_t rows_drawn = 0;
+ size_t tb_rows = tb->height / ( Style.font.height + SPACING );
+ size_t tb_cols = tb->width / Style.font.width;
+
+ for( size_t l = 0; l < tb->text.num_lines; l++ ) {
+ const Line & line = tb->text.lines[ ( tb->text.head + tb->text.num_lines - tb->scroll_offset - l ) % SCROLLBACK_SIZE ];
+
+ size_t rows = 1 + line.len / tb_cols;
+ if( line.len > 0 && line.len % tb_cols == 0 )
+ rows--;
+
+ for( size_t i = 0; i < line.len; i++ ) {
+ const Glyph & glyph = line.glyphs[ i ];
+
+ size_t row = i / tb_cols;
+ if( i > 0 && i % tb_cols == 0 )
+ row--;
+
+ int left = ( i % tb_cols ) * Style.font.width;
+ // TODO: wrapping doesn't update top correctly
+ int top = tb->height - ( 1 + rows_drawn + row ) * ( Style.font.height + SPACING );
+ if( top < 0 )
+ continue;
+
+ XSetFont( UI.display, UI.gc, ( glyph.bold ? Style.fontBold : Style.font ).font->fid );
+ XSetForeground( UI.display, UI.gc,
+ glyph.fg == SYSTEM ? Style.Colours.system : Style.colours[ glyph.bold ][ glyph.fg ] );
+ XDrawString( UI.display, doublebuf, UI.gc, left, top + Style.font.ascent + SPACING, &glyph.ch, 1 );
+ }
+
+ rows_drawn += rows;
+ if( rows_drawn >= tb_rows )
+ break;
+ }
+
+ XCopyArea( UI.display, doublebuf, UI.window, UI.gc, 0, 0, tb->width, tb->height, tb->x, tb->y );
+ XFreePixmap( UI.display, doublebuf );
+}
+
+void TextBox::scroll( int offset ) {
+ if( offset < 0 ) {
+ scroll_offset -= min( size_t( -offset ), scroll_offset );
+ }
+ else {
+ scroll_offset = min( scroll_offset + offset, text.num_lines );
+ }
+
+ textbox_draw( this );
+}
+
+void TextBox::page_down() {
+ size_t rows = height / ( Style.font.height + SPACING );
+ scroll( -int( rows ) - 2 );
+}
+
+void TextBox::page_up() {
+ size_t rows = height / ( Style.font.height + SPACING );
+ scroll( rows - 2 ); // TODO
+}
diff --git a/src/textbox.h b/src/textbox.h
@@ -1,9 +1,7 @@
-#ifndef _TEXTBOX_H_
-#define _TEXTBOX_H_
+#pragma once
-typedef enum
-{
- BLACK = 0,
+enum Colour {
+ BLACK,
RED,
GREEN,
YELLOW,
@@ -13,54 +11,49 @@ typedef enum
WHITE,
SYSTEM,
NONE,
-} Colour;
+};
-typedef struct Text Text;
-struct Text
-{
- char* buffer;
- unsigned int len;
+constexpr size_t MAX_LINE_LENGTH = 2048;
+constexpr size_t SCROLLBACK_SIZE = 1 << 16; // TODO
- Colour fg;
- Colour bg;
+// TODO: pack these better
+struct Glyph {
+ char ch;
+ Colour fg, bg;
bool bold;
-
- Text* next;
};
-typedef struct
-{
- Text* head;
- Text* tail;
-
- unsigned int len;
-} Line;
+struct Line {
+ Glyph glyphs[ MAX_LINE_LENGTH ];
+ size_t len = 0;
+};
-typedef struct
-{
- Line* lines;
+struct Text {
+ Line * lines;
+ size_t head;
+ size_t num_lines;
+};
- int maxLines;
- int numLines;
- int head;
+struct TextBox {
+ Text text;
int x;
int y;
int width;
int height;
- int rows;
- int cols;
+ size_t scroll_offset;
- int scrollDelta;
-} TextBox;
+ void scroll( int offset );
+ void page_up();
+ void page_down();
+};
-TextBox* textbox_new( unsigned int maxLines );
-void textbox_free( TextBox* self );
-void textbox_setpos( TextBox* self, int x, int y );
-void textbox_setsize( TextBox* self, int width, int height );
-void textbox_add( TextBox* self, const char* str, unsigned int len, Colour fg, Colour bg, bool bold );
-void textbox_newline( TextBox* self );
-void textbox_draw( TextBox* self );
+void textbox_init( TextBox * tb );
+void textbox_term( TextBox * tb );
-#endif // _TEXTBOX_H_
+void textbox_setpos( TextBox * tb, int x, int y );
+void textbox_setsize( TextBox * tb, int width, int height );
+void textbox_add( TextBox * tb, const char * str, unsigned int len, Colour fg, Colour bg, bool bold );
+void textbox_newline( TextBox * tb );
+void textbox_draw( const TextBox * tb );
diff --git a/src/ui.c b/src/ui.c
@@ -1,461 +0,0 @@
-#include <stdio.h>
-#include <stdlib.h>
-#include <err.h>
-
-#include <X11/Xutil.h>
-#include <X11/XKBlib.h>
-#include <X11/cursorfont.h>
-#include <X11/keysym.h>
-
-#include "common.h"
-#include "input.h"
-#include "script.h"
-
-Atom wmDeleteWindow;
-
-typedef struct {
- char c;
-
- Colour fg;
- bool bold;
-} StatusChar;
-
-static StatusChar* statusContents = NULL;
-static size_t statusCapacity = 256;
-static size_t statusLen = 0;
-
-void ui_statusDraw()
-{
- XSetForeground( UI.display, UI.gc, Style.statusBG );
- XFillRectangle( UI.display, UI.window, UI.gc, 0, UI.height - ( PADDING * 4 ) - ( Style.font.height * 2 ), UI.width, Style.font.height + ( PADDING * 2 ) );
-
- for( size_t i = 0; i < statusLen; i++ )
- {
- StatusChar sc = statusContents[ i ];
-
- XSetFont( UI.display, UI.gc, ( sc.bold ? Style.fontBold : Style.font ).font->fid );
- XSetForeground( UI.display, UI.gc, Style.colours[ sc.bold ][ sc.fg ] );
-
- int x = PADDING + i * Style.font.width;
- int y = UI.height - ( PADDING * 3 ) - Style.font.height - Style.font.descent;
- XDrawString( UI.display, UI.window, UI.gc, x, y, &sc.c, 1 );
- }
-}
-
-void ui_statusClear()
-{
- statusLen = 0;
-}
-
-void ui_statusAdd( const char c, const Colour fg, const bool bold )
-{
- if( ( statusLen + 1 ) * sizeof( StatusChar ) > statusCapacity )
- {
- size_t newcapacity = statusCapacity * 2;
- StatusChar* newcontents = realloc( statusContents, newcapacity );
-
- if( !newcontents )
- {
- return err( 1, "oom" );
- }
-
- statusContents = newcontents;
- statusCapacity = newcapacity;
- }
-
- statusContents[ statusLen ] = ( StatusChar ) { c, fg, bold };
- statusLen++;
-}
-
-void ui_draw()
-{
- XClearWindow( UI.display, UI.window );
-
- input_draw();
- ui_statusDraw();
-
- textbox_draw( UI.textChat );
- textbox_draw( UI.textMain );
-
- int spacerY = ( 2 * PADDING ) + ( Style.font.height + SPACING ) * CHAT_ROWS;
- XSetForeground( UI.display, UI.gc, Style.statusBG );
- XFillRectangle( UI.display, UI.window, UI.gc, 0, spacerY, UI.width, 1 );
-}
-
-void eventButtonPress( XEvent* event )
-{
- PRETEND_TO_USE( event );
-}
-
-void eventButtonRelease( XEvent* event )
-{
- PRETEND_TO_USE( event );
-}
-
-void eventMessage( XEvent* event )
-{
- if( ( Atom ) event->xclient.data.l[ 0 ] == wmDeleteWindow )
- {
- script_handleClose();
- }
-}
-
-void eventResize( XEvent* event )
-{
- int newWidth = event->xconfigure.width;
- int newHeight = event->xconfigure.height;
-
- if( newWidth == UI.width && newHeight == UI.height )
- {
- return;
- }
-
- UI.width = newWidth;
- UI.height = newHeight;
-
- XSetForeground( UI.display, UI.gc, Style.bg );
- XFillRectangle( UI.display, UI.window, UI.gc, 0, 0, UI.width, UI.height );
-
- textbox_setpos( UI.textChat, PADDING, PADDING );
- textbox_setsize( UI.textChat, UI.width - ( 2 * PADDING ), ( Style.font.height + SPACING ) * CHAT_ROWS );
-
- textbox_setpos( UI.textMain, PADDING, ( PADDING * 2 ) + CHAT_ROWS * ( Style.font.height + SPACING ) + 1 );
- textbox_setsize( UI.textMain, UI.width - ( 2 * PADDING ), UI.height
- - ( ( ( Style.font.height + SPACING ) * CHAT_ROWS ) + ( PADDING * 2 ) )
- - ( ( Style.font.height * 2 ) + ( PADDING * 5 ) ) - 1
- );
-}
-
-void eventExpose( XEvent* event )
-{
- PRETEND_TO_USE( event );
-
- ui_draw();
-}
-
-void eventKeyPress( XEvent* event )
-{
- #define ADD_MACRO( key, name ) \
- case key: \
- script_doMacro( name, sizeof( name ) - 1, shift, ctrl, alt ); \
- break
-
- XKeyEvent* keyEvent = &event->xkey;
-
- char keyBuffer[ 32 ];
- KeySym key;
-
- bool shift = keyEvent->state & ShiftMask;
- bool ctrl = keyEvent->state & ControlMask;
- bool alt = keyEvent->state & Mod1Mask;
-
- int len = XLookupString( keyEvent, keyBuffer, sizeof( keyBuffer ), &key, NULL );
-
- switch( key )
- {
- case XK_Return:
- {
- input_send();
-
- break;
- }
-
- case XK_BackSpace:
- input_backspace();
-
- break;
-
- case XK_Delete:
- input_delete();
-
- break;
-
- case XK_Page_Up:
- if( UI.textMain->scrollDelta < UI.textMain->numLines )
- {
- int toScroll = shift ? 1 : ( UI.textMain->rows - 2 );
-
- UI.textMain->scrollDelta = MIN( UI.textMain->scrollDelta + toScroll, UI.textMain->numLines );
-
- textbox_draw( UI.textMain );
- }
-
- break;
-
- case XK_Page_Down:
- if( UI.textMain->scrollDelta > 0 )
- {
- int toScroll = shift ? 1 : ( UI.textMain->rows - 2 );
-
- UI.textMain->scrollDelta = MAX( UI.textMain->scrollDelta - toScroll, 0 );
-
- textbox_draw( UI.textMain );
- }
-
- break;
-
- case XK_Up:
- input_up();
-
- break;
- case XK_Down:
- input_down();
-
- break;
-
- case XK_Left:
- input_left();
-
- break;
-
- case XK_Right:
- input_right();
-
- break;
-
- ADD_MACRO( XK_KP_1, "kp1" );
- ADD_MACRO( XK_KP_End, "kp1" );
-
- ADD_MACRO( XK_KP_2, "kp2" );
- ADD_MACRO( XK_KP_Down, "kp2" );
-
- ADD_MACRO( XK_KP_3, "kp3" );
- ADD_MACRO( XK_KP_Page_Down, "kp3" );
-
- ADD_MACRO( XK_KP_4, "kp4" );
- ADD_MACRO( XK_KP_Left, "kp4" );
-
- ADD_MACRO( XK_KP_5, "kp5" );
- ADD_MACRO( XK_KP_Begin, "kp5" );
-
- ADD_MACRO( XK_KP_6, "kp6" );
- ADD_MACRO( XK_KP_Right, "kp6" );
-
- ADD_MACRO( XK_KP_7, "kp7" );
- ADD_MACRO( XK_KP_Home, "kp7" );
-
- ADD_MACRO( XK_KP_8, "kp8" );
- ADD_MACRO( XK_KP_Up, "kp8" );
-
- ADD_MACRO( XK_KP_9, "kp9" );
- ADD_MACRO( XK_KP_Page_Up, "kp9" );
-
- ADD_MACRO( XK_KP_0, "kp0" );
- ADD_MACRO( XK_KP_Insert, "kp0" );
-
- ADD_MACRO( XK_KP_Multiply, "kp*" );
- ADD_MACRO( XK_KP_Divide, "kp/" );
- ADD_MACRO( XK_KP_Subtract, "kp-" );
- ADD_MACRO( XK_KP_Add, "kp+" );
-
- ADD_MACRO( XK_KP_Delete, "kp." );
- ADD_MACRO( XK_KP_Decimal, "kp." );
-
- ADD_MACRO( XK_F1, "f1" );
- ADD_MACRO( XK_F2, "f2" );
- ADD_MACRO( XK_F3, "f3" );
- ADD_MACRO( XK_F4, "f4" );
- ADD_MACRO( XK_F5, "f5" );
- ADD_MACRO( XK_F6, "f6" );
- ADD_MACRO( XK_F7, "f7" );
- ADD_MACRO( XK_F8, "f8" );
- ADD_MACRO( XK_F9, "f9" );
- ADD_MACRO( XK_F10, "f10" );
- ADD_MACRO( XK_F11, "f11" );
- ADD_MACRO( XK_F12, "f12" );
-
- default:
- if( ctrl || alt )
- {
- script_doMacro( keyBuffer, len, shift, ctrl, alt );
- }
- else
- {
- input_add( keyBuffer, len );
- }
-
- break;
- }
-
- #undef ADD_MACRO
-}
-
-void eventFocusOut( XEvent* event ) {
- PRETEND_TO_USE( event );
-
- UI.hasFocus = 0;
-}
-
-void eventFocusIn( XEvent* event )
-{
- PRETEND_TO_USE( event );
-
- UI.hasFocus = 1;
-
- XWMHints* hints = XGetWMHints( UI.display, UI.window );
- hints->flags &= ~XUrgencyHint;
- XSetWMHints( UI.display, UI.window, hints );
- XFree( hints );
-}
-
-void ( *EventHandler[ LASTEvent ] ) ( XEvent* ) =
-{
- [ ButtonPress ] = eventButtonPress,
- [ ButtonRelease ] = eventButtonRelease,
- [ ClientMessage ] = eventMessage,
- [ ConfigureNotify ] = eventResize,
- [ Expose ] = eventExpose,
- [ KeyPress ] = eventKeyPress,
- [ FocusOut ] = eventFocusOut,
- [ FocusIn ] = eventFocusIn,
-};
-
-void ui_handleXEvents()
-{
- while( XPending( UI.display ) )
- {
- XEvent event;
- XNextEvent( UI.display, &event );
-
- if( EventHandler[ event.type ] )
- {
- EventHandler[ event.type ]( &event );
- }
- }
-}
-
-MudFont loadFont( char* fontStr )
-{
- MudFont font;
-
- font.font = XLoadQueryFont( UI.display, fontStr );
-
- if( !font.font )
- {
- printf( "could not load font %s\n", fontStr );
-
- exit( 1 );
- }
-
- font.ascent = font.font->ascent;
- font.descent = font.font->descent;
- font.lbearing = font.font->min_bounds.lbearing;
- font.rbearing = font.font->max_bounds.rbearing;
-
- font.width = font.rbearing - font.lbearing;
- font.height = font.ascent + font.descent;
-
- return font;
-}
-
-void initStyle()
-{
- #define SETCOLOR( x, c ) \
- do { \
- XColor color; \
- XAllocNamedColor( UI.display, UI.colorMap, c, &color, &color ); \
- x = color.pixel; \
- } while( false )
- #define SETXCOLOR( x, y, c ) \
- do { \
- XColor color; \
- XAllocNamedColor( UI.display, UI.colorMap, c, &color, &color ); \
- x = color.pixel; \
- y = color; \
- } while( false )
-
- SETXCOLOR( Style.bg, Style.xBG, "#1a1a1a" );
- SETXCOLOR( Style.fg, Style.xFG, "#b6c2c4" );
-
- SETCOLOR( Style.cursor, "#00ff00" );
-
- SETCOLOR( Style.statusBG, "#333333" );
- SETCOLOR( Style.statusFG, "#ffffff" );
-
- SETCOLOR( Style.Colours.black, "#1a1a1a" );
- SETCOLOR( Style.Colours.red, "#ca4433" );
- SETCOLOR( Style.Colours.green, "#178a3a" );
- SETCOLOR( Style.Colours.yellow, "#dc7c2a" );
- SETCOLOR( Style.Colours.blue, "#415e87" );
- SETCOLOR( Style.Colours.magenta, "#5e468c" );
- SETCOLOR( Style.Colours.cyan, "#35789b" );
- SETCOLOR( Style.Colours.white, "#b6c2c4" );
-
- SETCOLOR( Style.Colours.lblack, "#666666" );
- SETCOLOR( Style.Colours.lred, "#ff2954" );
- SETCOLOR( Style.Colours.lgreen, "#5dd030" );
- SETCOLOR( Style.Colours.lyellow, "#fafc4f" );
- SETCOLOR( Style.Colours.lblue, "#3581e1" );
- SETCOLOR( Style.Colours.lmagenta, "#875fff" );
- SETCOLOR( Style.Colours.lcyan, "#29fbff" );
- SETCOLOR( Style.Colours.lwhite, "#cedbde" );
-
- SETCOLOR( Style.Colours.system, "#ffffff" );
-
- #undef SETCOLOR
- #undef SETXCOLOR
-
- Style.font = loadFont( "-windows-dina-medium-r-normal--12-120-75-75-c-0-microsoft-cp1252" );
- Style.fontBold = loadFont( "-windows-dina-bold-r-normal--12-120-75-75-c-0-microsoft-cp1252" );
-}
-
-void ui_init()
-{
- UI.display = XOpenDisplay( NULL );
- UI.screen = XDefaultScreen( UI.display );
- UI.width = -1;
- UI.height = -1;
-
- Window root = XRootWindow( UI.display, UI.screen );
- UI.depth = XDefaultDepth( UI.display, UI.screen );
- Visual* visual = XDefaultVisual( UI.display, UI.screen );
- UI.colorMap = XDefaultColormap( UI.display, UI.screen );
-
- UI.textMain = textbox_new( OUTPUT_MAX_LINES );
- UI.textChat = textbox_new( OUTPUT_MAX_LINES );
- statusContents = malloc( statusCapacity * sizeof( StatusChar ) );
-
- if( statusContents == NULL ) {
- err( 1, "oom" );
- }
-
- initStyle();
-
- XSetWindowAttributes attr =
- {
- .background_pixel = Style.bg,
- .event_mask = ExposureMask | StructureNotifyMask | KeyPressMask | ButtonPressMask | ButtonReleaseMask | FocusChangeMask,
- .colormap = UI.colorMap,
- };
-
- UI.window = XCreateWindow( UI.display, root, 0, 0, 800, 600, 0, UI.depth, InputOutput, visual, CWBackPixel | CWEventMask | CWColormap, &attr );
- UI.gc = XCreateGC( UI.display, UI.window, 0, NULL );
-
- XWMHints* hints = XAllocWMHints();
- XSetWMHints( UI.display, UI.window, hints );
- XFree( hints );
-
- Cursor cursor = XCreateFontCursor( UI.display, XC_xterm );
- XDefineCursor( UI.display, UI.window, cursor );
- XRecolorCursor( UI.display, cursor, &Style.xFG, &Style.xBG );
-
- XStoreName( UI.display, UI.window, "Mud Gangster" );
- XMapWindow( UI.display, UI.window );
-
- wmDeleteWindow = XInternAtom( UI.display, "WM_DELETE_WINDOW", false );
- XSetWMProtocols( UI.display, UI.window, &wmDeleteWindow, 1 );
-}
-
-void ui_end()
-{
- textbox_free( UI.textMain );
- textbox_free( UI.textChat );
- free( statusContents );
-
- XFreeFont( UI.display, Style.font.font );
- XFreeFont( UI.display, Style.fontBold.font );
-
- XFreeGC( UI.display, UI.gc );
- XDestroyWindow( UI.display, UI.window );
- XCloseDisplay( UI.display );
-}
diff --git a/src/ui.cc b/src/ui.cc
@@ -0,0 +1,401 @@
+#include <stdio.h>
+#include <stdlib.h>
+#include <err.h>
+
+#include <X11/Xutil.h>
+#include <X11/XKBlib.h>
+#include <X11/cursorfont.h>
+#include <X11/keysym.h>
+
+#include "common.h"
+#include "input.h"
+#include "script.h"
+
+UIDefs UI;
+StyleDefs Style;
+
+static Atom wmDeleteWindow;
+
+typedef struct {
+ char c;
+
+ Colour fg;
+ bool bold;
+} StatusChar;
+
+static StatusChar* statusContents = NULL;
+static size_t statusCapacity = 256;
+static size_t statusLen = 0;
+
+void ui_statusDraw() {
+ XSetForeground( UI.display, UI.gc, Style.statusBG );
+ XFillRectangle( UI.display, UI.window, UI.gc, 0, UI.height - ( PADDING * 4 ) - ( Style.font.height * 2 ), UI.width, Style.font.height + ( PADDING * 2 ) );
+
+ for( size_t i = 0; i < statusLen; i++ )
+ {
+ StatusChar sc = statusContents[ i ];
+
+ XSetFont( UI.display, UI.gc, ( sc.bold ? Style.fontBold : Style.font ).font->fid );
+ XSetForeground( UI.display, UI.gc, Style.colours[ sc.bold ][ sc.fg ] );
+
+ int x = PADDING + i * Style.font.width;
+ int y = UI.height - ( PADDING * 3 ) - Style.font.height - Style.font.descent;
+ XDrawString( UI.display, UI.window, UI.gc, x, y, &sc.c, 1 );
+ }
+}
+
+void ui_statusClear() {
+ statusLen = 0;
+}
+
+void ui_statusAdd( const char c, const Colour fg, const bool bold ) {
+ if( ( statusLen + 1 ) * sizeof( StatusChar ) > statusCapacity ) {
+ size_t newcapacity = statusCapacity * 2;
+ StatusChar * newcontents = ( StatusChar * ) realloc( statusContents, newcapacity );
+
+ if( !newcontents )
+ return err( 1, "oom" );
+
+ statusContents = newcontents;
+ statusCapacity = newcapacity;
+ }
+
+ statusContents[ statusLen ] = ( StatusChar ) { c, fg, bold };
+ statusLen++;
+}
+
+void ui_draw() {
+ XClearWindow( UI.display, UI.window );
+
+ input_draw();
+ ui_statusDraw();
+
+ textbox_draw( &UI.textChat );
+ textbox_draw( &UI.textMain );
+
+ int spacerY = ( 2 * PADDING ) + ( Style.font.height + SPACING ) * CHAT_ROWS;
+ XSetForeground( UI.display, UI.gc, Style.statusBG );
+ XFillRectangle( UI.display, UI.window, UI.gc, 0, spacerY, UI.width, 1 );
+}
+
+void eventButtonPress( XEvent* event ) { }
+
+void eventButtonRelease( XEvent* event ) { }
+
+void eventMessage( XEvent* event ) {
+ if( ( Atom ) event->xclient.data.l[ 0 ] == wmDeleteWindow ) {
+ script_handleClose();
+ }
+}
+
+void eventResize( XEvent* event ) {
+ int newWidth = event->xconfigure.width;
+ int newHeight = event->xconfigure.height;
+
+ if( newWidth == UI.width && newHeight == UI.height )
+ return;
+
+ UI.width = newWidth;
+ UI.height = newHeight;
+
+ XSetForeground( UI.display, UI.gc, Style.bg );
+ XFillRectangle( UI.display, UI.window, UI.gc, 0, 0, UI.width, UI.height );
+
+ textbox_setpos( &UI.textChat, PADDING, PADDING );
+ textbox_setsize( &UI.textChat, UI.width - ( 2 * PADDING ), ( Style.font.height + SPACING ) * CHAT_ROWS );
+
+ textbox_setpos( &UI.textMain, PADDING, ( PADDING * 2 ) + CHAT_ROWS * ( Style.font.height + SPACING ) + 1 );
+ textbox_setsize( &UI.textMain, UI.width - ( 2 * PADDING ), UI.height
+ - ( ( ( Style.font.height + SPACING ) * CHAT_ROWS ) + ( PADDING * 2 ) )
+ - ( ( Style.font.height * 2 ) + ( PADDING * 5 ) ) - 1
+ );
+}
+
+void eventExpose( XEvent* event ) {
+ ui_draw();
+}
+
+void eventKeyPress( XEvent* event ) {
+ #define ADD_MACRO( key, name ) \
+ case key: \
+ script_doMacro( name, sizeof( name ) - 1, shift, ctrl, alt ); \
+ break
+
+ XKeyEvent* keyEvent = &event->xkey;
+
+ char keyBuffer[ 32 ];
+ KeySym key;
+
+ bool shift = keyEvent->state & ShiftMask;
+ bool ctrl = keyEvent->state & ControlMask;
+ bool alt = keyEvent->state & Mod1Mask;
+
+ int len = XLookupString( keyEvent, keyBuffer, sizeof( keyBuffer ), &key, NULL );
+
+ switch( key ) {
+ case XK_Return:
+ input_send();
+ break;
+
+ case XK_BackSpace:
+ input_backspace();
+ break;
+
+ case XK_Delete:
+ input_delete();
+ break;
+
+ case XK_Page_Up:
+ if( shift )
+ UI.textMain.scroll( 1 );
+ else
+ UI.textMain.page_up();
+ break;
+
+ case XK_Page_Down:
+ if( shift )
+ UI.textMain.scroll( -1 );
+ else
+ UI.textMain.page_down();
+ break;
+
+ case XK_Up:
+ input_up();
+ break;
+
+ case XK_Down:
+ input_down();
+ break;
+
+ case XK_Left:
+ input_left();
+ break;
+
+ case XK_Right:
+ input_right();
+ break;
+
+ ADD_MACRO( XK_KP_1, "kp1" );
+ ADD_MACRO( XK_KP_End, "kp1" );
+
+ ADD_MACRO( XK_KP_2, "kp2" );
+ ADD_MACRO( XK_KP_Down, "kp2" );
+
+ ADD_MACRO( XK_KP_3, "kp3" );
+ ADD_MACRO( XK_KP_Page_Down, "kp3" );
+
+ ADD_MACRO( XK_KP_4, "kp4" );
+ ADD_MACRO( XK_KP_Left, "kp4" );
+
+ ADD_MACRO( XK_KP_5, "kp5" );
+ ADD_MACRO( XK_KP_Begin, "kp5" );
+
+ ADD_MACRO( XK_KP_6, "kp6" );
+ ADD_MACRO( XK_KP_Right, "kp6" );
+
+ ADD_MACRO( XK_KP_7, "kp7" );
+ ADD_MACRO( XK_KP_Home, "kp7" );
+
+ ADD_MACRO( XK_KP_8, "kp8" );
+ ADD_MACRO( XK_KP_Up, "kp8" );
+
+ ADD_MACRO( XK_KP_9, "kp9" );
+ ADD_MACRO( XK_KP_Page_Up, "kp9" );
+
+ ADD_MACRO( XK_KP_0, "kp0" );
+ ADD_MACRO( XK_KP_Insert, "kp0" );
+
+ ADD_MACRO( XK_KP_Multiply, "kp*" );
+ ADD_MACRO( XK_KP_Divide, "kp/" );
+ ADD_MACRO( XK_KP_Subtract, "kp-" );
+ ADD_MACRO( XK_KP_Add, "kp+" );
+
+ ADD_MACRO( XK_KP_Delete, "kp." );
+ ADD_MACRO( XK_KP_Decimal, "kp." );
+
+ ADD_MACRO( XK_F1, "f1" );
+ ADD_MACRO( XK_F2, "f2" );
+ ADD_MACRO( XK_F3, "f3" );
+ ADD_MACRO( XK_F4, "f4" );
+ ADD_MACRO( XK_F5, "f5" );
+ ADD_MACRO( XK_F6, "f6" );
+ ADD_MACRO( XK_F7, "f7" );
+ ADD_MACRO( XK_F8, "f8" );
+ ADD_MACRO( XK_F9, "f9" );
+ ADD_MACRO( XK_F10, "f10" );
+ ADD_MACRO( XK_F11, "f11" );
+ ADD_MACRO( XK_F12, "f12" );
+
+ default:
+ if( ctrl || alt )
+ script_doMacro( keyBuffer, len, shift, ctrl, alt );
+ else
+ input_add( keyBuffer, len );
+
+ break;
+ }
+
+ #undef ADD_MACRO
+}
+
+void eventFocusOut( XEvent* event ) {
+ UI.hasFocus = 0;
+}
+
+void eventFocusIn( XEvent* event ) {
+ UI.hasFocus = 1;
+
+ XWMHints* hints = XGetWMHints( UI.display, UI.window );
+ hints->flags &= ~XUrgencyHint;
+ XSetWMHints( UI.display, UI.window, hints );
+ XFree( hints );
+}
+
+void ui_handleXEvents() {
+ void ( *EventHandler[ LASTEvent ] )( XEvent* ) = { };
+ EventHandler[ ButtonPress ] = eventButtonPress;
+ EventHandler[ ButtonRelease ] = eventButtonRelease;
+ EventHandler[ ClientMessage ] = eventMessage;
+ EventHandler[ ConfigureNotify ] = eventResize;
+ EventHandler[ Expose ] = eventExpose;
+ EventHandler[ KeyPress ] = eventKeyPress;
+ EventHandler[ FocusOut ] = eventFocusOut;
+ EventHandler[ FocusIn ] = eventFocusIn;
+
+ while( XPending( UI.display ) ) {
+ XEvent event;
+ XNextEvent( UI.display, &event );
+
+ if( EventHandler[ event.type ] != NULL )
+ EventHandler[ event.type ]( &event );
+ }
+}
+
+MudFont loadFont( const char * fontStr ) {
+ MudFont font;
+
+ font.font = XLoadQueryFont( UI.display, fontStr );
+
+ if( !font.font ) {
+ printf( "could not load font %s\n", fontStr );
+
+ exit( 1 );
+ }
+
+ font.ascent = font.font->ascent;
+ font.descent = font.font->descent;
+ font.lbearing = font.font->min_bounds.lbearing;
+ font.rbearing = font.font->max_bounds.rbearing;
+
+ font.width = font.rbearing - font.lbearing;
+ font.height = font.ascent + font.descent;
+
+ return font;
+}
+
+void initStyle() {
+ #define SETCOLOR( x, c ) \
+ do { \
+ XColor color; \
+ XAllocNamedColor( UI.display, UI.colorMap, c, &color, &color ); \
+ x = color.pixel; \
+ } while( false )
+ #define SETXCOLOR( x, y, c ) \
+ do { \
+ XColor color; \
+ XAllocNamedColor( UI.display, UI.colorMap, c, &color, &color ); \
+ x = color.pixel; \
+ y = color; \
+ } while( false )
+
+ SETXCOLOR( Style.bg, Style.xBG, "#1a1a1a" );
+ SETXCOLOR( Style.fg, Style.xFG, "#b6c2c4" );
+
+ SETCOLOR( Style.cursor, "#00ff00" );
+
+ SETCOLOR( Style.statusBG, "#333333" );
+ SETCOLOR( Style.statusFG, "#ffffff" );
+
+ SETCOLOR( Style.Colours.black, "#1a1a1a" );
+ SETCOLOR( Style.Colours.red, "#ca4433" );
+ SETCOLOR( Style.Colours.green, "#178a3a" );
+ SETCOLOR( Style.Colours.yellow, "#dc7c2a" );
+ SETCOLOR( Style.Colours.blue, "#415e87" );
+ SETCOLOR( Style.Colours.magenta, "#5e468c" );
+ SETCOLOR( Style.Colours.cyan, "#35789b" );
+ SETCOLOR( Style.Colours.white, "#b6c2c4" );
+
+ SETCOLOR( Style.Colours.lblack, "#666666" );
+ SETCOLOR( Style.Colours.lred, "#ff2954" );
+ SETCOLOR( Style.Colours.lgreen, "#5dd030" );
+ SETCOLOR( Style.Colours.lyellow, "#fafc4f" );
+ SETCOLOR( Style.Colours.lblue, "#3581e1" );
+ SETCOLOR( Style.Colours.lmagenta, "#875fff" );
+ SETCOLOR( Style.Colours.lcyan, "#29fbff" );
+ SETCOLOR( Style.Colours.lwhite, "#cedbde" );
+
+ SETCOLOR( Style.Colours.system, "#ffffff" );
+
+ #undef SETCOLOR
+ #undef SETXCOLOR
+
+ Style.font = loadFont( "-windows-dina-medium-r-normal--10-*-*-*-c-0-*-*" );
+ Style.fontBold = loadFont( "-windows-dina-bold-r-normal--10-*-*-*-c-0-*-*" );
+}
+
+void ui_init() {
+ textbox_init( &UI.textMain );
+ textbox_init( &UI.textChat );
+ UI.display = XOpenDisplay( NULL );
+ UI.screen = XDefaultScreen( UI.display );
+ UI.width = -1;
+ UI.height = -1;
+
+ Window root = XRootWindow( UI.display, UI.screen );
+ UI.depth = XDefaultDepth( UI.display, UI.screen );
+ Visual * visual = XDefaultVisual( UI.display, UI.screen );
+ UI.colorMap = XDefaultColormap( UI.display, UI.screen );
+
+ statusContents = ( StatusChar * ) malloc( statusCapacity * sizeof( StatusChar ) );
+
+ if( statusContents == NULL ) {
+ err( 1, "oom" );
+ }
+
+ initStyle();
+
+ XSetWindowAttributes attr = { };
+ attr.background_pixel = Style.bg,
+ attr.event_mask = ExposureMask | StructureNotifyMask | KeyPressMask | ButtonPressMask | ButtonReleaseMask | FocusChangeMask,
+ attr.colormap = UI.colorMap,
+
+ UI.window = XCreateWindow( UI.display, root, 0, 0, 800, 600, 0, UI.depth, InputOutput, visual, CWBackPixel | CWEventMask | CWColormap, &attr );
+ UI.gc = XCreateGC( UI.display, UI.window, 0, NULL );
+
+ XWMHints* hints = XAllocWMHints();
+ XSetWMHints( UI.display, UI.window, hints );
+ XFree( hints );
+
+ Cursor cursor = XCreateFontCursor( UI.display, XC_xterm );
+ XDefineCursor( UI.display, UI.window, cursor );
+ XRecolorCursor( UI.display, cursor, &Style.xFG, &Style.xBG );
+
+ XStoreName( UI.display, UI.window, "Mud Gangster" );
+ XMapWindow( UI.display, UI.window );
+
+ wmDeleteWindow = XInternAtom( UI.display, "WM_DELETE_WINDOW", false );
+ XSetWMProtocols( UI.display, UI.window, &wmDeleteWindow, 1 );
+}
+
+void ui_end() {
+ textbox_term( &UI.textMain );
+ textbox_term( &UI.textChat );
+ free( statusContents );
+
+ XFreeFont( UI.display, Style.font.font );
+ XFreeFont( UI.display, Style.fontBold.font );
+
+ XFreeGC( UI.display, UI.gc );
+ XDestroyWindow( UI.display, UI.window );
+ XCloseDisplay( UI.display );
+}