commit fb71710ac31d7efd90ca285e98d9bf472e27ba7b
parent b09af99d67088a1036836ad25ecfa5cab79f3fc8
Author: Michael Savage <mikejsavage@gmail.com>
Date: Mon, 3 Sep 2018 20:30:20 +0300
Move all xlib stuff into x11.cc
Diffstat:
make.lua | | | 2 | +- |
src/common.h | | | 101 | ++++++++++++++----------------------------------------------------------------- |
src/input.cc | | | 68 | +++++++++++++++++++++----------------------------------------------- |
src/input.h | | | 23 | ++++++++++++++--------- |
src/main.cc | | | 6 | +++--- |
src/script.cc | | | 46 | +++++++++++++++++----------------------------- |
src/script.h | | | 8 | +++----- |
src/textbox.cc | | | 126 | ++++++++++--------------------------------------------------------------------- |
src/textbox.h | | | 61 | +++++++++++++++---------------------------------------------- |
src/ui.cc | | | 400 | ------------------------------------------------------------------------------- |
src/ui.h | | | 27 | ++++++++++++++++++--------- |
src/x11.cc | | | 639 | +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ |
12 files changed, 765 insertions(+), 742 deletions(-)
diff --git a/make.lua b/make.lua
@@ -5,5 +5,5 @@ require( "scripts.gen_makefile" )
-- require( "libs.lua" )
-- require( "libs.lpeg" )
-bin( "mudGangster", { "src/main", "src/script", "src/textbox", "src/input", "src/ui" } )
+bin( "mudGangster", { "src/main", "src/script", "src/textbox", "src/input", "src/x11" } )
gcc_bin_ldflags( "mudGangster", "-lm -lX11 -llua" ) -- -Wl,-E" ) need to export symbols when vendoring
diff --git a/src/common.h b/src/common.h
@@ -1,92 +1,12 @@
#pragma once
+#include <stdio.h>
#include <stdint.h>
+#include <stddef.h>
+#include <stdlib.h>
#include <assert.h>
-#include <X11/Xlib.h>
-
#include "config.h"
-#include "textbox.h"
-
-struct UIDefs {
- Display * display;
- int screen;
-
- GC gc;
- Colormap colorMap;
-
- Window window;
-
- TextBox textMain;
- TextBox textChat;
-
- int width;
- int height;
- int depth;
-
- int hasFocus;
-};
-
-extern UIDefs UI;
-
-struct MudFont {
- int ascent;
- int descent;
-
- short lbearing;
- short rbearing;
-
- int height;
- int width;
-
- XFontStruct * font;
-};
-
-struct StyleDefs {
- ulong bg;
- ulong fg;
-
- XColor xBG;
- XColor xFG;
-
- ulong statusBG;
- ulong statusFG;
-
- ulong inputBG;
- ulong inputFG;
- ulong cursor;
-
- MudFont font;
- MudFont fontBold;
-
- union {
- struct {
- ulong black;
- ulong red;
- ulong green;
- ulong yellow;
- ulong blue;
- ulong magenta;
- ulong cyan;
- ulong white;
-
- ulong lblack;
- ulong lred;
- ulong lgreen;
- ulong lyellow;
- ulong lblue;
- ulong lmagenta;
- ulong lcyan;
- ulong lwhite;
-
- ulong system;
- } Colours;
-
- ulong colours[ 2 ][ 8 ];
- };
-};
-
-extern StyleDefs Style;
#define STRL( x ) ( x ), sizeof( x ) - 1
@@ -108,3 +28,18 @@ inline To checked_cast( const From & from ) {
assert( From( result ) == from );
return result;
}
+
+// TODO: probably don't need most of these
+enum Colour {
+ BLACK,
+ RED,
+ GREEN,
+ YELLOW,
+ BLUE,
+ MAGENTA,
+ CYAN,
+ WHITE,
+ SYSTEM,
+
+ NUM_COLOURS,
+};
diff --git a/src/input.cc b/src/input.cc
@@ -23,7 +23,27 @@ static int inputBufferSize = 256;
static int inputLen = 0;
static int inputPos = 0;
-void input_send() {
+void input_init() {
+ inputBuffer = ( char * ) malloc( inputBufferSize );
+ starsBuffer = ( char * ) malloc( inputBufferSize );
+
+ memset( starsBuffer, '*', inputBufferSize );
+}
+
+void input_term() {
+ free( inputBuffer );
+ free( starsBuffer );
+}
+
+InputBuffer input_get_buffer() {
+ InputBuffer buf;
+ buf.buf = inputBuffer;
+ buf.len = inputLen;
+ buf.cursor_pos = inputPos;
+ return buf;
+}
+
+void input_return() {
if( inputLen > 0 ) {
InputHistory * lastCmd = &inputHistory[ ( inputHistoryHead + inputHistoryCount - 1 ) % MAX_INPUT_HISTORY ];
@@ -52,8 +72,6 @@ void input_send() {
inputLen = 0;
inputPos = 0;
-
- input_draw();
}
void input_backspace() {
@@ -63,8 +81,6 @@ void input_backspace() {
inputLen--;
inputPos--;
}
-
- input_draw();
}
void input_delete() {
@@ -73,8 +89,6 @@ void input_delete() {
inputLen--;
}
-
- input_draw();
}
void input_up() {
@@ -90,8 +104,6 @@ void input_up() {
inputLen = cmd.len;
inputPos = cmd.len;
-
- input_draw();
}
void input_down() {
@@ -114,20 +126,14 @@ void input_down() {
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( const char * buffer, int len ) {
@@ -148,36 +154,4 @@ void input_add( const char * buffer, int 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/input.h b/src/input.h
@@ -1,8 +1,18 @@
-#ifndef _INPUT_H_
-#define _INPUT_H_
+#pragma once
-void input_send();
+#include <stddef.h>
+struct InputBuffer {
+ char * buf;
+
+ size_t len;
+ size_t cursor_pos;
+};
+
+void input_init();
+void input_term();
+
+void input_return();
void input_backspace();
void input_delete();
@@ -13,9 +23,4 @@ void input_right();
void input_add( const char * buffer, int len );
-void input_draw();
-
-void input_init();
-void input_end();
-
-#endif // _INPUT_H_
+InputBuffer input_get_buffer();
diff --git a/src/main.cc b/src/main.cc
@@ -9,9 +9,9 @@ int main() {
// main loop is done in lua
- script_end();
- input_end();
- ui_end();
+ script_term();
+ input_term();
+ ui_term();
return 0;
}
diff --git a/src/script.cc b/src/script.cc
@@ -1,13 +1,8 @@
-#include <stdlib.h>
-#include <assert.h>
-
-#include <lua.hpp>
-
-#include <X11/Xutil.h>
-
#include "common.h"
-#include "ui.h"
#include "platform.h"
+#include "ui.h"
+
+#include <lua.hpp>
#if LUA_VERSION_NUM < 502
#define luaL_len lua_objlen
@@ -59,7 +54,8 @@ extern "C" int mud_handleXEvents( lua_State * ) {
return 0;
}
-static void generic_print( TextBox * tb, lua_State * L ) {
+template< typename F >
+static void generic_print( F * f, lua_State * L ) {
const char * str = luaL_checkstring( L, 1 );
size_t len = luaL_len( L, 1 );
@@ -67,36 +63,36 @@ static void generic_print( TextBox * tb, lua_State * L ) {
Colour bg = Colour( luaL_checkinteger( L, 3 ) );
bool bold = lua_toboolean( L, 4 );
- textbox_add( tb, str, len, fg, bg, bold );
+ f( str, len, fg, bg, bold );
}
extern "C" int mud_printMain( lua_State * L ) {
- generic_print( &UI.textMain, L );
+ generic_print( ui_main_print, L );
return 0;
}
extern "C" int mud_newlineMain( lua_State * L ) {
- textbox_newline( &UI.textMain );
+ ui_main_newline();
return 0;
}
extern "C" int mud_drawMain( lua_State * L ) {
- textbox_draw( &UI.textMain );
+ ui_main_draw();
return 0;
}
extern "C" int mud_printChat( lua_State * L ) {
- generic_print( &UI.textChat, L );
+ generic_print( ui_chat_print, L );
return 0;
}
extern "C" int mud_newlineChat( lua_State * L ) {
- textbox_newline( &UI.textChat );
+ ui_chat_newline();
return 0;
}
extern "C" int mud_drawChat( lua_State * L ) {
- textbox_draw( &UI.textChat );
+ ui_chat_draw();
return 0;
}
@@ -105,7 +101,7 @@ extern "C" int mud_setStatus( lua_State * L ) {
size_t len = luaL_len( L, 1 );
- ui_statusClear();
+ ui_clear_status();
for( size_t i = 0; i < len; i++ ) {
lua_pushnumber( L, i + 1 );
@@ -131,7 +127,7 @@ extern "C" int mud_setStatus( lua_State * L ) {
lua_pop( L, 4 );
}
- ui_statusDraw();
+ ui_draw_status();
return 0;
}
@@ -149,13 +145,7 @@ extern "C" int mud_setHandlers( lua_State * L ) {
}
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 );
- }
-
+ ui_urgent();
return 0;
}
@@ -182,11 +172,10 @@ void script_init() {
if( luaL_loadbufferx( lua, ( const char * ) lua_bytecode, sizeof( lua_bytecode ), "main", "b" ) != LUA_OK ) {
printf( "Error reading main.lua: %s\n", lua_tostring( lua, -1 ) );
-
exit( 1 );
}
- lua_pushinteger( lua, ConnectionNumber( UI.display ) );
+ lua_pushinteger( lua, ui_display_fd() );
lua_pushcfunction( lua, mud_handleXEvents );
lua_pushcfunction( lua, mud_printMain );
@@ -205,11 +194,10 @@ void script_init() {
if( lua_pcall( lua, 11, 0, -13 ) ) {
printf( "Error running main.lua: %s\n", lua_tostring( lua, -1 ) );
-
exit( 1 );
}
}
-void script_end() {
+void script_term() {
lua_close( lua );
}
diff --git a/src/script.h b/src/script.h
@@ -1,11 +1,9 @@
-#ifndef _SCRIPT_H_
-#define _SCRIPT_H_
+#pragma once
+// TODO: should be size_t here?
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();
-void script_end();
-
-#endif // _SCRIPT_H_
+void script_term();
diff --git a/src/textbox.cc b/src/textbox.cc
@@ -1,10 +1,7 @@
-#include <stdlib.h>
#include <string.h>
-#include <assert.h>
-
-#include <X11/Xlib.h>
#include "common.h"
+#include "textbox.h"
static uint8_t pack_style( Colour fg, Colour bg, bool bold ) {
STATIC_ASSERT( NUM_COLOURS * NUM_COLOURS * 2 < UINT8_MAX );
@@ -18,7 +15,7 @@ static uint8_t pack_style( Colour fg, Colour bg, bool bold ) {
return checked_cast< uint8_t >( style );
}
-static void unpack_style( uint8_t style, int * fg, int * bg, int * bold ) {
+void unpack_style( uint8_t style, int * fg, int * bg, int * bold ) {
*bold = style % 2;
style /= 2;
@@ -28,35 +25,24 @@ static void unpack_style( uint8_t style, int * fg, int * bg, int * bold ) {
*fg = style;
}
-void textbox_init( TextBox * tb, size_t scrollback ) {
- *tb = { };
+void text_init( TextBuffer * tb, size_t scrollback ) {
// TODO: this is kinda crap
- tb->text.lines = ( Line * ) calloc( sizeof( Line ), scrollback );
- tb->text.num_lines = 1;
- tb->text.max_lines = scrollback;
-}
-
-void textbox_term( TextBox * tb ) {
- free( tb->text.lines );
-}
-
-void textbox_setpos( TextBox * tb, int x, int y ) {
- tb->x = x;
- tb->y = y;
+ tb->lines = ( TextBuffer::Line * ) calloc( sizeof( TextBuffer::Line ), scrollback );
+ tb->num_lines = 1;
+ tb->max_lines = scrollback;
}
-void textbox_setsize( TextBox * tb, int width, int height ) {
- tb->width = width;
- tb->height = height;
+void text_destroy( TextBuffer * tb ) {
+ free( tb->lines );
}
-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 ) % tb->text.max_lines ];
+void text_add( TextBuffer * tb, const char * str, size_t len, Colour fg, Colour bg, bool bold ) {
+ TextBuffer::Line * line = &tb->lines[ ( tb->head + tb->num_lines ) % tb->max_lines ];
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 ];
+ TextBuffer::Glyph & glyph = line->glyphs[ line->len + i ];
glyph.ch = str[ i ];
glyph.style = pack_style( fg, bg, bold );
};
@@ -64,93 +50,13 @@ void textbox_add( TextBox * tb, const char * str, unsigned int len, Colour fg, C
line->len += n;
}
-void textbox_newline( TextBox * tb ) {
- if( tb->text.num_lines < tb->text.max_lines ) {
- tb->text.num_lines++;
+void text_newline( TextBuffer * tb ) {
+ if( tb->num_lines < tb->max_lines ) {
+ tb->num_lines++;
return;
}
- tb->text.head++;
- Line * line = &tb->text.lines[ ( tb->text.head + tb->text.num_lines ) % tb->text.max_lines ];
+ tb->head++;
+ TextBuffer::Line * line = &tb->lines[ ( tb->head + tb->num_lines ) % tb->max_lines ];
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 );
-
- /*
- * lines refers to lines of text sent from the game
- * rows refers to visual rows of text in the client, so when lines get
- * wrapped they have more than one row
- */
- size_t lines_drawn = 0;
- size_t rows_drawn = 0;
- size_t tb_rows = tb->height / ( Style.font.height + SPACING );
- size_t tb_cols = tb->width / Style.font.width;
-
- while( rows_drawn < tb_rows && lines_drawn < tb->text.num_lines ) {
- const Line & line = tb->text.lines[ ( tb->text.head + tb->text.num_lines - tb->scroll_offset - lines_drawn ) % tb->text.max_lines ];
-
- size_t line_rows = 1 + line.len / tb_cols;
- if( line.len > 0 && line.len % tb_cols == 0 )
- line_rows--;
-
- for( size_t i = 0; i < line.len; i++ ) {
- const Glyph & glyph = line.glyphs[ i ];
-
- size_t row = i / tb_cols;
-
- int left = ( i % tb_cols ) * Style.font.width;
- int top = tb->height - ( rows_drawn + line_rows - row ) * ( Style.font.height + SPACING );
- if( top < 0 )
- continue;
-
- int fg, bg, bold;
- unpack_style( glyph.style, &fg, &bg, &bold );
-
- // bg
- int top_spacing = SPACING / 2;
- int bot_spacing = SPACING - top_spacing;
- XSetForeground( UI.display, UI.gc, bg == SYSTEM ? Style.Colours.system : Style.colours[ 0 ][ bg ] );
- XFillRectangle( UI.display, doublebuf, UI.gc, left, top - top_spacing, Style.font.width, Style.font.height + bot_spacing );
-
- // fg
- XSetFont( UI.display, UI.gc, ( bold ? Style.fontBold : Style.font ).font->fid );
- XSetForeground( UI.display, UI.gc, fg == SYSTEM ? Style.Colours.system : Style.colours[ bold ][ fg ] );
- XDrawString( UI.display, doublebuf, UI.gc, left, top + Style.font.ascent + SPACING, &glyph.ch, 1 );
- }
-
- lines_drawn++;
- rows_drawn += line_rows;
- }
-
- 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( TextBox * tb, int offset ) {
- if( offset < 0 ) {
- tb->scroll_offset -= min( size_t( -offset ), tb->scroll_offset );
- }
- else {
- tb->scroll_offset = min( tb->scroll_offset + offset, tb->text.num_lines - 1 );
- }
-
- textbox_draw( tb );
-}
-
-void textbox_page_down( TextBox * tb ) {
- size_t rows = tb->height / ( Style.font.height + SPACING );
- textbox_scroll( tb, -int( rows ) + 1 );
-}
-
-void textbox_page_up( TextBox * tb ) {
- size_t rows = tb->height / ( Style.font.height + SPACING );
- textbox_scroll( tb, rows - 1 );
-}
diff --git a/src/textbox.h b/src/textbox.h
@@ -1,62 +1,31 @@
#pragma once
-#include <stdint.h>
-
-// TODO: probably don't need most of these
-enum Colour {
- BLACK,
- RED,
- GREEN,
- YELLOW,
- BLUE,
- MAGENTA,
- CYAN,
- WHITE,
- SYSTEM,
-
- NUM_COLOURS,
-};
+#include "common.h"
constexpr size_t MAX_LINE_LENGTH = 2048;
constexpr size_t SCROLLBACK_SIZE = 1 << 16;
-struct Glyph {
- char ch;
- uint8_t style;
-};
+struct TextBuffer {
+ struct Glyph {
+ char ch;
+ uint8_t style;
+ };
-struct Line {
- Glyph glyphs[ MAX_LINE_LENGTH ];
- size_t len = 0;
-};
+ struct Line {
+ Glyph glyphs[ MAX_LINE_LENGTH ];
+ size_t len = 0;
+ };
-struct Text {
Line * lines;
size_t head;
size_t num_lines;
size_t max_lines;
};
-struct TextBox {
- Text text;
-
- int x;
- int y;
- int width;
- int height;
-
- size_t scroll_offset;
-};
-
-void textbox_init( TextBox * tb, size_t scrollback );
-void textbox_term( TextBox * tb );
+void text_init( TextBuffer * tb, size_t scrollback );
-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 );
+void text_add( TextBuffer * tb, const char * str, size_t len, Colour fg, Colour bg, bool bold );
+void text_newline( TextBuffer * tb );
+void unpack_style( uint8_t style, int * fg, int * bg, int * bold );
-void textbox_scroll( TextBox * tb, int offset );
-void textbox_page_up( TextBox * tb );
-void textbox_page_down( TextBox * tb );
+void text_destroy( TextBuffer * tb );
diff --git a/src/ui.cc b/src/ui.cc
@@ -1,400 +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"
-
-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 );
-}
-
-static void eventButtonPress( XEvent * event ) { }
-
-static void eventButtonRelease( XEvent * event ) { }
-
-static void eventMessage( XEvent * event ) {
- if( ( Atom ) event->xclient.data.l[ 0 ] == wmDeleteWindow ) {
- script_handleClose();
- }
-}
-
-static 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
- );
-}
-
-static void eventExpose( XEvent * event ) {
- ui_draw();
-}
-
-static 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 )
- textbox_scroll( &UI.textMain, 1 );
- else
- textbox_page_up( &UI.textMain );
- break;
-
- case XK_Page_Down:
- if( shift )
- textbox_scroll( &UI.textMain, -1 );
- else
- textbox_page_down( &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
-}
-
-static void eventFocusOut( XEvent * event ) {
- UI.hasFocus = 0;
-}
-
-static 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 );
- }
-}
-
-static 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;
-}
-
-static 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, SCROLLBACK_SIZE );
- textbox_init( &UI.textChat, CHAT_ROWS );
- 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 );
-}
diff --git a/src/ui.h b/src/ui.h
@@ -1,17 +1,26 @@
-#ifndef _UI_H_
-#define _UI_H_
+#pragma once
#include "common.h"
-void ui_handleXEvents();
+void ui_handleXEvents(); // TODO: very x11 specific!
-void ui_statusDraw();
-void ui_statusClear();
-void ui_statusAdd( const char c, const Colour fg, const bool bold );
+void ui_draw_status();
+void ui_clear_status();
+void ui_statusAdd( char c, Colour fg, bool bold );
void ui_draw();
-void ui_init();
-void ui_end();
+void ui_main_draw();
+void ui_main_newline();
+void ui_main_print( const char * str, size_t len, Colour fg, Colour bg, bool bold );
+
+void ui_chat_draw();
+void ui_chat_newline();
+void ui_chat_print( const char * str, size_t len, Colour fg, Colour bg, bool bold );
+
+bool ui_urgent();
-#endif // _UI_H_
+int ui_display_fd(); // TODO: very x11 specific!
+
+void ui_init();
+void ui_term();
diff --git a/src/x11.cc b/src/x11.cc
@@ -0,0 +1,639 @@
+#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"
+#include "textbox.h"
+
+struct TextBufferView {
+ TextBuffer text;
+
+ int x;
+ int y;
+ int width;
+ int height;
+
+ size_t scroll_offset;
+};
+
+struct {
+ Display * display;
+ int screen;
+
+ GC gc;
+ Colormap colorMap;
+
+ Window window;
+
+ TextBufferView main_text_view;
+ TextBufferView chat_text_view;
+
+ int width;
+ int height;
+ int depth;
+
+ bool has_focus;
+} UI;
+
+struct MudFont {
+ int ascent;
+ int descent;
+
+ int height;
+ int width;
+
+ XFontStruct * font;
+};
+
+struct {
+ ulong bg;
+ ulong fg;
+
+ XColor xBG;
+ XColor xFG;
+
+ ulong statusBG;
+ ulong statusFG;
+
+ ulong inputBG;
+ ulong inputFG;
+ ulong cursor;
+
+ MudFont font;
+ MudFont fontBold;
+
+ union {
+ struct {
+ ulong black;
+ ulong red;
+ ulong green;
+ ulong yellow;
+ ulong blue;
+ ulong magenta;
+ ulong cyan;
+ ulong white;
+
+ ulong lblack;
+ ulong lred;
+ ulong lgreen;
+ ulong lyellow;
+ ulong lblue;
+ ulong lmagenta;
+ ulong lcyan;
+ ulong lwhite;
+
+ ulong system;
+ } Colours;
+
+ ulong colours[ 2 ][ 8 ];
+ };
+} Style;
+
+static void textview_draw( const TextBufferView * tv ) {
+ if( tv->width == 0 || tv->height == 0 )
+ return;
+
+ Pixmap doublebuf = XCreatePixmap( UI.display, UI.window, tv->width, tv->height, UI.depth );
+
+ XSetForeground( UI.display, UI.gc, Style.bg );
+ XFillRectangle( UI.display, doublebuf, UI.gc, 0, 0, tv->width, tv->height );
+
+ /*
+ * lines refers to lines of text sent from the game
+ * rows refers to visual rows of text in the client, so when lines get
+ * wrapped they have more than one row
+ */
+ size_t lines_drawn = 0;
+ size_t rows_drawn = 0;
+ size_t tv_rows = tv->height / ( Style.font.height + SPACING );
+ size_t tv_cols = tv->width / Style.font.width;
+
+ while( rows_drawn < tv_rows && lines_drawn < tv->text.num_lines ) {
+ const TextBuffer::Line & line = tv->text.lines[ ( tv->text.head + tv->text.num_lines - tv->scroll_offset - lines_drawn ) % tv->text.max_lines ];
+
+ size_t line_rows = 1 + line.len / tv_cols;
+ if( line.len > 0 && line.len % tv_cols == 0 )
+ line_rows--;
+
+ for( size_t i = 0; i < line.len; i++ ) {
+ const TextBuffer::Glyph & glyph = line.glyphs[ i ];
+
+ size_t row = i / tv_cols;
+
+ int left = ( i % tv_cols ) * Style.font.width;
+ int top = tv->height - ( rows_drawn + line_rows - row ) * ( Style.font.height + SPACING );
+ if( top < 0 )
+ continue;
+
+ int fg, bg, bold;
+ unpack_style( glyph.style, &fg, &bg, &bold );
+
+ // bg
+ int top_spacing = SPACING / 2;
+ int bot_spacing = SPACING - top_spacing;
+ XSetForeground( UI.display, UI.gc, bg == SYSTEM ? Style.Colours.system : Style.colours[ 0 ][ bg ] );
+ XFillRectangle( UI.display, doublebuf, UI.gc, left, top - top_spacing, Style.font.width, Style.font.height + bot_spacing );
+
+ // fg
+ XSetFont( UI.display, UI.gc, ( bold ? Style.fontBold : Style.font ).font->fid );
+ XSetForeground( UI.display, UI.gc, fg == SYSTEM ? Style.Colours.system : Style.colours[ bold ][ fg ] );
+ XDrawString( UI.display, doublebuf, UI.gc, left, top + Style.font.ascent + SPACING, &glyph.ch, 1 );
+ }
+
+ lines_drawn++;
+ rows_drawn += line_rows;
+ }
+
+ XCopyArea( UI.display, doublebuf, UI.window, UI.gc, 0, 0, tv->width, tv->height, tv->x, tv->y );
+ XFreePixmap( UI.display, doublebuf );
+}
+
+static void textview_scroll( TextBufferView * tv, int offset ) {
+ if( offset < 0 ) {
+ tv->scroll_offset -= min( size_t( -offset ), tv->scroll_offset );
+ }
+ else {
+ tv->scroll_offset = min( tv->scroll_offset + offset, tv->text.num_lines - 1 );
+ }
+
+ textview_draw( tv );
+}
+
+static void textview_page_down( TextBufferView * tv ) {
+ size_t rows = tv->height / ( Style.font.height + SPACING );
+ textview_scroll( tv, -int( rows ) + 1 );
+}
+
+static void textview_page_up( TextBufferView * tv ) {
+ size_t rows = tv->height / ( Style.font.height + SPACING );
+ textview_scroll( tv, rows - 1 );
+}
+
+static void textview_set_pos( TextBufferView * tv, int x, int y ) {
+ tv->x = x;
+ tv->y = y;
+}
+
+static void textview_set_size( TextBufferView * tv, int w, int h ) {
+ tv->width = w;
+ tv->height = h;
+}
+
+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_clear_status() {
+ 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_status() {
+ 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 draw_input() {
+ InputBuffer input = input_get_buffer();
+
+ 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 ), input.buf, input.len );
+
+ XSetForeground( UI.display, UI.gc, Style.cursor );
+ XFillRectangle( UI.display, UI.window, UI.gc, PADDING + Style.font.width * input.cursor_pos, UI.height - ( PADDING + Style.font.height ), Style.font.width, Style.font.height );
+
+ if( input.cursor_pos < input.len ) {
+ XSetForeground( UI.display, UI.gc, Style.bg );
+ XDrawString( UI.display, UI.window, UI.gc, PADDING + Style.font.width * input.cursor_pos, UI.height - ( PADDING + Style.font.descent ), input.buf + input.cursor_pos, 1 );
+ }
+}
+
+void ui_draw() {
+ XClearWindow( UI.display, UI.window );
+
+ draw_input();
+ ui_draw_status();
+
+ textview_draw( &UI.chat_text_view );
+ textview_draw( &UI.main_text_view );
+
+ 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 );
+}
+
+static void eventButtonPress( XEvent * event ) { }
+
+static void eventButtonRelease( XEvent * event ) { }
+
+static void eventMessage( XEvent * event ) {
+ if( ( Atom ) event->xclient.data.l[ 0 ] == wmDeleteWindow ) {
+ script_handleClose();
+ }
+}
+
+static 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 );
+
+ textview_set_pos( &UI.chat_text_view, PADDING, PADDING );
+ textview_set_size( &UI.chat_text_view, UI.width - ( 2 * PADDING ), ( Style.font.height + SPACING ) * CHAT_ROWS );
+
+ textview_set_pos( &UI.main_text_view, PADDING, ( PADDING * 2 ) + CHAT_ROWS * ( Style.font.height + SPACING ) + 1 );
+ textview_set_size( &UI.main_text_view, UI.width - ( 2 * PADDING ), UI.height
+ - ( ( ( Style.font.height + SPACING ) * CHAT_ROWS ) + ( PADDING * 2 ) )
+ - ( ( Style.font.height * 2 ) + ( PADDING * 5 ) ) - 1
+ );
+}
+
+static void eventExpose( XEvent * event ) {
+ ui_draw();
+}
+
+static 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_return();
+ draw_input();
+ break;
+
+ case XK_BackSpace:
+ input_backspace();
+ draw_input();
+ break;
+
+ case XK_Delete:
+ input_delete();
+ draw_input();
+ break;
+
+ case XK_Page_Up:
+ if( shift )
+ textview_scroll( &UI.main_text_view, 1 );
+ else
+ textview_page_up( &UI.main_text_view );
+ break;
+
+ case XK_Page_Down:
+ if( shift )
+ textview_scroll( &UI.main_text_view, -1 );
+ else
+ textview_page_down( &UI.main_text_view );
+ break;
+
+ case XK_Up:
+ input_up();
+ draw_input();
+ break;
+
+ case XK_Down:
+ input_down();
+ draw_input();
+ break;
+
+ case XK_Left:
+ input_left();
+ draw_input();
+ break;
+
+ case XK_Right:
+ input_right();
+ draw_input();
+ 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 );
+ draw_input();
+ }
+
+ break;
+ }
+
+ #undef ADD_MACRO
+}
+
+static void eventFocusOut( XEvent * event ) {
+ UI.has_focus = false;
+}
+
+static void eventFocusIn( XEvent * event ) {
+ UI.has_focus = true;
+
+ 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 );
+ }
+}
+
+static 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.width = font.font->max_bounds.rbearing - font.font->min_bounds.lbearing;
+ font.height = font.ascent + font.descent;
+
+ return font;
+}
+
+static 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() {
+ UI = { };
+
+ text_init( &UI.main_text_view.text, SCROLLBACK_SIZE );
+ text_init( &UI.chat_text_view.text, CHAT_ROWS );
+ 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_main_draw() {
+ textview_draw( &UI.main_text_view );
+}
+
+void ui_main_newline() {
+ text_newline( &UI.main_text_view.text );
+}
+
+void ui_main_print( const char * str, size_t len, Colour fg, Colour bg, bool bold ) {
+ text_add( &UI.main_text_view.text, str, len, fg, bg, bold );
+}
+
+void ui_chat_draw() {
+ textview_draw( &UI.chat_text_view );
+}
+
+void ui_chat_newline() {
+ text_newline( &UI.chat_text_view.text );
+}
+
+void ui_chat_print( const char * str, size_t len, Colour fg, Colour bg, bool bold ) {
+ text_add( &UI.chat_text_view.text, str, len, fg, bg, bold );
+}
+
+void ui_urgent() {
+ if( !UI.has_focus ) {
+ XWMHints * hints = XGetWMHints( UI.display, UI.window );
+ hints->flags |= XUrgencyHint;
+ XSetWMHints( UI.display, UI.window, hints );
+ XFree( hints );
+ }
+}
+
+int ui_display_fd() {
+ return ConnectionNumber( UI.display );
+}
+
+void ui_term() {
+ text_destroy( &UI.main_text_view.text );
+ text_destroy( &UI.chat_text_view.text );
+ 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 );
+}