diff options
Diffstat (limited to 'project-base/src/linux/platform.c')
| -rw-r--r-- | project-base/src/linux/platform.c | 1788 |
1 files changed, 1788 insertions, 0 deletions
diff --git a/project-base/src/linux/platform.c b/project-base/src/linux/platform.c new file mode 100644 index 0000000..5d4e249 --- /dev/null +++ b/project-base/src/linux/platform.c @@ -0,0 +1,1788 @@ +/* +* BSD 2-Clause “Simplified” License +* Copyright (c) 2019, Aldrik Ramaekers, aldrik.ramaekers@protonmail.com +* All rights reserved. +*/ + +#include <sys/times.h> +#include <sys/vtimes.h> +#include <locale.h> +#include <stdio.h> +#include <stdlib.h> +#include <X11/X.h> +#include <X11/Xlib.h> +#include <X11/Xatom.h> +#include <time.h> +#include <X11/XKBlib.h> +#include <unistd.h> +#include <limits.h> +#include <dirent.h> +#include <errno.h> +#include <dlfcn.h> +#include <sys/stat.h> +#include <sys/mman.h> +#include <X11/cursorfont.h> + +#define GET_ATOM(X) window->X = XInternAtom(window->display, #X, False) + +typedef struct t_backbuffer +{ + s32 width; + s32 height; + u8 *buffer; // 4bytes color + 1byte depth + XImage * s_image; +} backbuffer; + +struct t_platform_window +{ + Display *display; + Window parent; + XVisualInfo *visual_info; + Colormap cmap; + Window window; + GLXContext gl_context; + XWindowAttributes window_attributes; + XEvent event; + char *clipboard_str; + GC gc; + s32 clipboard_strlen; + + Atom xdnd_req; + Atom xdnd_source; + Atom XdndEnter; + Atom XdndPosition; + Atom XdndStatus; + Atom XdndTypeList; + Atom XdndActionCopy; + Atom XdndDrop; + Atom XdndFinished; + Atom XdndSelection; + Atom XdndLeave; + Atom quit; + Atom PRIMARY; + Atom CLIPBOARD; + Atom UTF8_STRING; + Atom COMPOUND_STRING; + Atom TARGETS; + Atom MULTIPLE; + Atom _NET_WM_STATE; + + // shared window properties + void (*update_func)(platform_window*); + void (*resize_func)(platform_window*,u32,u32); + keyboard_input keyboard; + mouse_input mouse; + camera camera; + bool icon_loaded; + bool do_draw; + backbuffer backbuffer; + s32 width; + s32 height; + bool is_open; + bool has_focus; + cursor_type curr_cursor_type; + cursor_type next_cursor_type; +}; + +bool platform_get_clipboard(platform_window *window, char *buffer) +{ + char *result; + unsigned long ressize, restail; + int resbits; + Atom bufid = XInternAtom(window->display, "CLIPBOARD", False), + fmtid = XInternAtom(window->display, "UTF8_STRING", False), + propid = XInternAtom(window->display, "XSEL_DATA", False), + incrid = XInternAtom(window->display, "INCR", False); + XEvent event; + + if(window->CLIPBOARD != None) { + for (s32 i = 0; i < window_registry.length; i++) { + platform_window* w = *(platform_window**)array_at(&window_registry, i); + + if (XGetSelectionOwner(window->display, window->CLIPBOARD) == w->window) { + snprintf(buffer, MAX_INPUT_LENGTH, "%s", w->clipboard_str); + return true; + } + } + } + + XConvertSelection(window->display, bufid, fmtid, propid, window->window, CurrentTime); + do { + XNextEvent(window->display, &event); + } while (event.type != SelectionNotify || event.xselection.selection != bufid); + + if (event.xselection.property) + { + XGetWindowProperty(window->display, window->window, propid, 0, LONG_MAX/4, False, AnyPropertyType, + &fmtid, &resbits, &ressize, &restail, (unsigned char**)&result); + + if (fmtid == incrid) + printf("Buffer is too large and INCR reading is not implemented yet.\n"); + else + snprintf(buffer, MAX_INPUT_LENGTH, "%s", result); + + XFree(result); + return True; + } + else // request failed, e.g. owner can't convert to the target format + return False; +} + +bool platform_set_clipboard(platform_window *window, char *buffer) +{ + if (buffer) + { + if(window->CLIPBOARD != None && XGetSelectionOwner(window->display, window->CLIPBOARD) != window->window) { + XSetSelectionOwner(window->display, window->CLIPBOARD, window->window, CurrentTime); + } + + window->clipboard_strlen = strlen(buffer); + if(!window->clipboard_str) { + window->clipboard_str = mem_alloc(window->clipboard_strlen+1); + } else { + window->clipboard_str = mem_realloc(window->clipboard_str, window->clipboard_strlen+1); + } + string_copyn(window->clipboard_str, buffer, window->clipboard_strlen+1); + + return true; + } + + return false; +} + +void platform_create_config_directory(char *directory) +{ + char *env = getenv("HOME"); + char tmp[PATH_MAX]; + snprintf(tmp, PATH_MAX, "%s/%s", env, directory); + + if (!platform_directory_exists(tmp)) + { + mkdir(tmp, S_IRWXU | S_IRWXG | S_IROTH | S_IXOTH); + } +} + +char* platform_get_config_save_location(char *buffer, char *directory) +{ + char *env = getenv("HOME"); + snprintf(buffer, PATH_MAX, "%s/%s%s", env, directory, "/config.txt"); + return buffer; +} + +inline void platform_set_cursor(platform_window *window, cursor_type type) +{ + if (window->next_cursor_type != type) + { + window->next_cursor_type = type; + } +} + +bool platform_get_active_directory(char *buffer) +{ + char cwd[PATH_MAX]; + if (getcwd(cwd, sizeof(cwd)) != NULL) { + string_copyn(buffer, cwd, PATH_MAX); + } else { + return false; + } + return true; +} + +bool platform_set_active_directory(char *path) +{ + return !chdir(path); +} + +bool platform_delete_file(char *path) +{ + return remove(path) == 0; +} + +bool platform_write_file_content(char *path, const char *mode, char *buffer, s32 len) +{ + bool result = false; + + FILE *file = fopen(path, mode); + + if (!file) + { + goto done_failure; + } + else + { + fwrite(buffer, 1, len, file); // TODO(Aldrik): is this correct? + //fprintf(file, "%s", buffer); + } + + //done: + fclose(file); + done_failure: + return result; +} + +void platform_window_set_title(platform_window *window, char *name) +{ + Atom WM_NAME = XInternAtom(window->display, "WM_NAME", False); + Atom _NET_WM_NAME = XInternAtom(window->display, "_NET_WM_NAME", False); + Atom _NET_WM_ICON_NAME = XInternAtom(window->display, "_NET_WM_ICON_NAME", False); + + char *list[1] = { (char *) name }; + XTextProperty property; + + XStoreName(window->display, window->window, name); + + Xutf8TextListToTextProperty(window->display, list, 1, XUTF8StringStyle, + &property); + XSetTextProperty(window->display, window->window, &property, WM_NAME); + XSetTextProperty(window->display, window->window, &property, _NET_WM_NAME); + XSetTextProperty(window->display, window->window, &property, XA_WM_NAME); + XSetTextProperty(window->display, window->window, &property, _NET_WM_ICON_NAME); + XFree(property.value); +} + +bool platform_file_exists(char *path) +{ + if(access(path, F_OK) != -1) { + return 1; + } + + return 0; +} + +bool platform_directory_exists(char *path) +{ + DIR* dir = opendir(path); + if (dir) { + /* Directory exists. */ + closedir(dir); + return 1; + } else if (ENOENT == errno) { + return 0; // does not exist + } else { + return 0; // error opening dir + } +} + + +s32 platform_get_file_size(char *path) +{ + FILE *file = fopen(path, "rb"); + if (!file) return -1; + + fseek(file, 0 , SEEK_END); + int length = ftell(file); + fseek(file, 0, SEEK_SET); + + fclose(file); + return length; +} + +file_content platform_read_file_content(char *path, const char *mode) +{ + file_content result; + result.content = 0; + result.content_length = 0; + result.file_error = 0; + + FILE *file = fopen(path, mode); + + if (!file) + { + if (errno == EMFILE) + result.file_error = FILE_ERROR_TOO_MANY_OPEN_FILES_PROCESS; + else if (errno == ENFILE) + result.file_error = FILE_ERROR_TOO_MANY_OPEN_FILES_SYSTEM; + else if (errno == EACCES) + result.file_error = FILE_ERROR_NO_ACCESS; + else if (errno == EPERM) + result.file_error = FILE_ERROR_NO_ACCESS; + else if (errno == ENOENT) + result.file_error = FILE_ERROR_NOT_FOUND; + else if (errno == ECONNABORTED) + result.file_error = FILE_ERROR_CONNECTION_ABORTED; + else if (errno == ECONNREFUSED) + result.file_error = FILE_ERROR_CONNECTION_REFUSED; + else if (errno == ENETDOWN) + result.file_error = FILE_ERROR_NETWORK_DOWN; + else if (errno == EREMOTEIO) + result.file_error = FILE_ERROR_REMOTE_IO_ERROR; + else if (errno == ESTALE) + result.file_error = FILE_ERROR_STALE; + else + { + result.file_error = FILE_ERROR_GENERIC; + printf("ERROR: %d\n", errno); + } + + goto done_failure; + } + + fseek(file, 0 , SEEK_END); + int length = ftell(file); + fseek(file, 0, SEEK_SET); + + s32 length_to_alloc = length+1; + + result.content = mem_alloc(length_to_alloc); + if (!result.content) goto done; + + memset(result.content, 0, length); + s32 read_result = fread(result.content, 1, length, file); + if (read_result == 0 && length != 0) + { + mem_free(result.content); + result.content = 0; + return result; + } + + result.content_length = read_result; + + ((char*)result.content)[length] = 0; + + done: + fclose(file); + done_failure: + return result; +} + +inline void platform_destroy_file_content(file_content *content) +{ + log_assert(content, "Content is null"); + mem_free(content->content); +} + +static s32 translate_keycode(platform_window *window, s32 scancode) +{ + s32 keySym; + + // Valid key code range is [8,255], according to the Xlib manual + + if (1) + { + // Try secondary keysym, for numeric keypad keys + // Note: This way we always force "NumLock = ON", which is intentional + // since the returned key code should correspond to a physical + // location. + keySym = XkbKeycodeToKeysym(window->display, scancode, 0, 1); + switch (keySym) + { + case XK_KP_0: return KEY_KP_0; + case XK_KP_1: return KEY_KP_1; + case XK_KP_2: return KEY_KP_2; + case XK_KP_3: return KEY_KP_3; + case XK_KP_4: return KEY_KP_4; + case XK_KP_5: return KEY_KP_5; + case XK_KP_6: return KEY_KP_6; + case XK_KP_7: return KEY_KP_7; + case XK_KP_8: return KEY_KP_8; + case XK_KP_9: return KEY_KP_9; + case XK_KP_Separator: + case XK_KP_Decimal: return KEY_KP_DECIMAL; + case XK_KP_Equal: return KEY_KP_EQUAL; + case XK_KP_Enter: return KEY_KP_ENTER; + default: break; + } + + // Now try primary keysym for function keys (non-printable keys) + // These should not depend on the current keyboard layout + keySym = XkbKeycodeToKeysym(window->display, scancode, 0, 0); + } + + switch (keySym) + { + case XK_Escape: return KEY_ESCAPE; + case XK_Tab: return KEY_TAB; + case XK_Shift_L: return KEY_LEFT_SHIFT; + case XK_Shift_R: return KEY_RIGHT_SHIFT; + case XK_Control_L: return KEY_LEFT_CONTROL; + case XK_Control_R: return KEY_RIGHT_CONTROL; + case XK_Meta_L: + case XK_Alt_L: return KEY_LEFT_ALT; + case XK_Mode_switch: // Mapped to Alt_R on many keyboards + case XK_ISO_Level3_Shift: // AltGr on at least some machines + case XK_Meta_R: + case XK_Alt_R: return KEY_RIGHT_ALT; + case XK_Super_L: return KEY_LEFT_SUPER; + case XK_Super_R: return KEY_RIGHT_SUPER; + case XK_Menu: return KEY_MENU; + case XK_Num_Lock: return KEY_NUM_LOCK; + case XK_Caps_Lock: return KEY_CAPS_LOCK; + case XK_Print: return KEY_PRINT_SCREEN; + case XK_Scroll_Lock: return KEY_SCROLL_LOCK; + case XK_Pause: return KEY_PAUSE; + case XK_Delete: return KEY_DELETE; + case XK_BackSpace: return KEY_BACKSPACE; + case XK_Return: return KEY_ENTER; + case XK_Home: return KEY_HOME; + case XK_End: return KEY_END; + case XK_Page_Up: return KEY_PAGE_UP; + case XK_Page_Down: return KEY_PAGE_DOWN; + case XK_Insert: return KEY_INSERT; + case XK_Left: return KEY_LEFT; + case XK_Right: return KEY_RIGHT; + case XK_Down: return KEY_DOWN; + case XK_Up: return KEY_UP; + case XK_F1: return KEY_F1; + case XK_F2: return KEY_F2; + case XK_F3: return KEY_F3; + case XK_F4: return KEY_F4; + case XK_F5: return KEY_F5; + case XK_F6: return KEY_F6; + case XK_F7: return KEY_F7; + case XK_F8: return KEY_F8; + case XK_F9: return KEY_F9; + case XK_F10: return KEY_F10; + case XK_F11: return KEY_F11; + case XK_F12: return KEY_F12; + case XK_F13: return KEY_F13; + case XK_F14: return KEY_F14; + case XK_F15: return KEY_F15; + case XK_F16: return KEY_F16; + case XK_F17: return KEY_F17; + case XK_F18: return KEY_F18; + case XK_F19: return KEY_F19; + case XK_F20: return KEY_F20; + case XK_F21: return KEY_F21; + case XK_F22: return KEY_F22; + case XK_F23: return KEY_F23; + case XK_F24: return KEY_F24; + case XK_F25: return KEY_F25; + + // Numeric keypad + case XK_KP_Divide: return KEY_KP_DIVIDE; + case XK_KP_Multiply: return KEY_KP_MULTIPLY; + case XK_KP_Subtract: return KEY_KP_SUBTRACT; + case XK_KP_Add: return KEY_KP_ADD; + + // These should have been detected in secondary keysym test above! + case XK_KP_Insert: return KEY_KP_0; + case XK_KP_End: return KEY_KP_1; + case XK_KP_Down: return KEY_KP_2; + case XK_KP_Page_Down: return KEY_KP_3; + case XK_KP_Left: return KEY_KP_4; + case XK_KP_Right: return KEY_KP_6; + case XK_KP_Home: return KEY_KP_7; + case XK_KP_Up: return KEY_KP_8; + case XK_KP_Page_Up: return KEY_KP_9; + case XK_KP_Delete: return KEY_KP_DECIMAL; + case XK_KP_Equal: return KEY_KP_EQUAL; + case XK_KP_Enter: return KEY_KP_ENTER; + + // Last resort: Check for printable keys (should not happen if the XKB + // extension is available). This will give a layout dependent mapping + // (which is wrong, and we may miss some keys, especially on non-US + // keyboards), but it's better than nothing... + case XK_a: return KEY_A; + case XK_b: return KEY_B; + case XK_c: return KEY_C; + case XK_d: return KEY_D; + case XK_e: return KEY_E; + case XK_f: return KEY_F; + case XK_g: return KEY_G; + case XK_h: return KEY_H; + case XK_i: return KEY_I; + case XK_j: return KEY_J; + case XK_k: return KEY_K; + case XK_l: return KEY_L; + case XK_m: return KEY_M; + case XK_n: return KEY_N; + case XK_o: return KEY_O; + case XK_p: return KEY_P; + case XK_q: return KEY_Q; + case XK_r: return KEY_R; + case XK_s: return KEY_S; + case XK_t: return KEY_T; + case XK_u: return KEY_U; + case XK_v: return KEY_V; + case XK_w: return KEY_W; + case XK_x: return KEY_X; + case XK_y: return KEY_Y; + case XK_z: return KEY_Z; + case XK_1: return KEY_1; + case XK_2: return KEY_2; + case XK_3: return KEY_3; + case XK_4: return KEY_4; + case XK_5: return KEY_5; + case XK_6: return KEY_6; + case XK_7: return KEY_7; + case XK_8: return KEY_8; + case XK_9: return KEY_9; + case XK_0: return KEY_0; + case XK_space: return KEY_SPACE; + case XK_minus: return KEY_MINUS; + case XK_equal: return KEY_EQUAL; + case XK_bracketleft: return KEY_LEFT_BRACKET; + case XK_bracketright: return KEY_RIGHT_BRACKET; + case XK_backslash: return KEY_BACKSLASH; + case XK_semicolon: return KEY_SEMICOLON; + case XK_apostrophe: return KEY_APOSTROPHE; + case XK_grave: return KEY_GRAVE_ACCENT; + case XK_comma: return KEY_COMMA; + case XK_period: return KEY_PERIOD; + case XK_slash: return KEY_SLASH; + case XK_less: return KEY_WORLD_1; // At least in some layouts... + default: break; + } + + // No matching translation was found + return KEY_UNKNOWN; +} + +static void create_key_tables(platform_window* window) +{ + s32 scancode; + XkbDescPtr desc = XkbGetMap(window->display, 0, XkbUseCoreKbd); + XkbGetNames(window->display, XkbKeyNamesMask, desc); + + // uncomment for layout independant input for games. +#if 0 + s32 key; + char name[XkbKeyNameLength + 1]; + for (scancode = desc->min_key_code; scancode <= desc->max_key_code; scancode++) + { + memcpy(name, desc->names->keys[scancode].name, XkbKeyNameLength); + name[XkbKeyNameLength] = '\0'; + + if (strcmp(name, "TLDE") == 0) key = KEY_GRAVE_ACCENT; + else if (strcmp(name, "AE01") == 0) key = KEY_1; + else if (strcmp(name, "AE02") == 0) key = KEY_2; + else if (strcmp(name, "AE03") == 0) key = KEY_3; + else if (strcmp(name, "AE04") == 0) key = KEY_4; + else if (strcmp(name, "AE05") == 0) key = KEY_5; + else if (strcmp(name, "AE06") == 0) key = KEY_6; + else if (strcmp(name, "AE07") == 0) key = KEY_7; + else if (strcmp(name, "AE08") == 0) key = KEY_8; + else if (strcmp(name, "AE09") == 0) key = KEY_9; + else if (strcmp(name, "AE10") == 0) key = KEY_0; + else if (strcmp(name, "AE11") == 0) key = KEY_MINUS; + else if (strcmp(name, "AE12") == 0) key = KEY_EQUAL; + else if (strcmp(name, "AD01") == 0) key = KEY_Q; + else if (strcmp(name, "AD02") == 0) key = KEY_W; + else if (strcmp(name, "AD03") == 0) key = KEY_E; + else if (strcmp(name, "AD04") == 0) key = KEY_R; + else if (strcmp(name, "AD05") == 0) key = KEY_T; + else if (strcmp(name, "AD06") == 0) key = KEY_Y; + else if (strcmp(name, "AD07") == 0) key = KEY_U; + else if (strcmp(name, "AD08") == 0) key = KEY_I; + else if (strcmp(name, "AD09") == 0) key = KEY_O; + else if (strcmp(name, "AD10") == 0) key = KEY_P; + else if (strcmp(name, "AD11") == 0) key = KEY_LEFT_BRACKET; + else if (strcmp(name, "AD12") == 0) key = KEY_RIGHT_BRACKET; + else if (strcmp(name, "AC01") == 0) key = KEY_A; + else if (strcmp(name, "AC02") == 0) key = KEY_S; + else if (strcmp(name, "AC03") == 0) key = KEY_D; + else if (strcmp(name, "AC04") == 0) key = KEY_F; + else if (strcmp(name, "AC05") == 0) key = KEY_G; + else if (strcmp(name, "AC06") == 0) key = KEY_H; + else if (strcmp(name, "AC07") == 0) key = KEY_J; + else if (strcmp(name, "AC08") == 0) key = KEY_K; + else if (strcmp(name, "AC09") == 0) key = KEY_L; + else if (strcmp(name, "AC10") == 0) key = KEY_SEMICOLON; + else if (strcmp(name, "AC11") == 0) key = KEY_APOSTROPHE; + else if (strcmp(name, "AB01") == 0) key = KEY_Z; + else if (strcmp(name, "AB02") == 0) key = KEY_X; + else if (strcmp(name, "AB03") == 0) key = KEY_C; + else if (strcmp(name, "AB04") == 0) key = KEY_V; + else if (strcmp(name, "AB05") == 0) key = KEY_B; + else if (strcmp(name, "AB06") == 0) key = KEY_N; + else if (strcmp(name, "AB07") == 0) key = KEY_M; + else if (strcmp(name, "AB08") == 0) key = KEY_COMMA; + else if (strcmp(name, "AB09") == 0) key = KEY_PERIOD; + else if (strcmp(name, "AB10") == 0) key = KEY_SLASH; + else if (strcmp(name, "BKSL") == 0) key = KEY_BACKSLASH; + else if (strcmp(name, "LSGT") == 0) key = KEY_WORLD_1; + else key = KEY_UNKNOWN; + + if ((scancode >= 0) && (scancode < 256)) + keycode_map[scancode] = key; + } +#endif + + for (scancode = 0; scancode < MAX_KEYCODE; scancode++) + { + // Translate the un-translated key codes using traditional X11 KeySym + // lookups + + keycode_map[scancode] = translate_keycode(window, scancode); + } + + XkbFreeNames(desc, XkbKeyNamesMask, True); + XkbFreeKeyboard(desc, 0, True); +} + +inline void platform_init(int argc, char **argv, char* config_path) +{ + setlocale(LC_ALL, "en_US.UTF-8"); + + XInitThreads(); + + _platform_init_shared(argc, argv, config_path); +} + +inline void platform_destroy() +{ + _platform_destroy_shared(); + memory_print_leaks(); +} + +inline void platform_window_make_current(platform_window *window) +{ + if (current_render_driver() == DRIVER_GL) + IMP_glXMakeCurrent(window->display, window->window, window->gl_context); +} + +static void _allocate_backbuffer(platform_window *window) +{ + if (window->backbuffer.buffer) { mem_free(window->backbuffer.buffer); window->backbuffer.buffer = 0; } + + window->backbuffer.width = window->width; + window->backbuffer.height = window->height+1; + + s32 bufferMemorySize = (window->backbuffer.width*window->backbuffer.height)*5; + window->backbuffer.buffer = mem_alloc(bufferMemorySize); + window->backbuffer.s_image = XCreateImage( + window->display, + DefaultVisual(window->display, DefaultScreen(window->display)), + DefaultDepth(window->display, DefaultScreen(window->display)), + ZPixmap, 0, (char*)window->backbuffer.buffer, + window->backbuffer.width, window->backbuffer.height, 32, 0); +} + +void platform_window_set_size(platform_window *window, u16 width, u16 height) +{ + XResizeWindow(window->display, window->window, width, height); + + if (current_render_driver() == DRIVER_CPU) + _allocate_backbuffer(window); +} + +void platform_window_set_position(platform_window *window, u16 x, u16 y) +{ + XMoveWindow(window->display, window->window, x, y); +} + + +vec2 platform_get_window_size(platform_window *window) +{ + vec2 res; + res.x = window->width; + res.y = window->height; + return res; +} + +void platform_setup_backbuffer(platform_window *window) +{ + if (current_render_driver() == DRIVER_GL) + { + static GLXContext share_list = 0; + + // get opengl context + window->gl_context = IMP_glXCreateContext(window->display, window->visual_info, + share_list, GL_TRUE); + + if (share_list == 0) + share_list = window->gl_context; + IMP_glXMakeCurrent(window->display, window->window, window->gl_context); + + } + else + { + _allocate_backbuffer(window); + } +} + +void platform_setup_renderer() +{ + if (current_render_driver() == DRIVER_GL) + { + ////// GL SETUP + IMP_glDepthMask(GL_TRUE); + IMP_glColorMask(GL_TRUE, GL_TRUE, GL_TRUE, GL_TRUE); + IMP_glDepthFunc(GL_LEQUAL); + IMP_glEnable(GL_DEPTH_TEST); + IMP_glAlphaFunc(GL_GREATER, 0.0f); + IMP_glEnable(GL_ALPHA_TEST); + IMP_glEnable(GL_SAMPLE_ALPHA_TO_COVERAGE); + IMP_glEnable(GL_SAMPLE_ALPHA_TO_ONE); + IMP_glEnable(GL_MULTISAMPLE); + IMP_glEnable(GL_TEXTURE_2D); + IMP_glEnable(GL_SCISSOR_TEST); + IMP_glEnable(GL_BLEND); + //IMP_glEnable(GL_FRAMEBUFFER_SRGB); + IMP_glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); + IMP_glEnable(GL_MULTISAMPLE_ARB); + + IMP_glMatrixMode(GL_TEXTURE); + IMP_glLoadIdentity(); + + IMP_glMatrixMode(GL_MODELVIEW); + IMP_glLoadIdentity(); + + IMP_glMatrixMode(GL_PROJECTION); + IMP_glLoadIdentity(); + //glOrtho(0, width, height, 0, -1, 1); + + IMP_glMatrixMode(GL_MODELVIEW); + + } +} + +int _x11_error_handler(Display *display, XErrorEvent *event) +{ + char tmp[2000]; + XGetErrorText(display, event->error_code, tmp, 2000); + printf("X11 ERROR: %s\n", tmp); + + return 0; +} + +bool platform_is_graphical() +{ + Display* display = XOpenDisplay(NULL); + if (!display) { + return false; + } + else { + XCloseDisplay(display); + return true; + } +} + +s32 platform_get_titlebar_height() +{ + return 0; // Lets hope this is consistent. +} + +platform_window* platform_open_window_ex(char *name, u16 width, u16 height, u16 max_w, u16 max_h, u16 min_w, u16 min_h, s32 flags, void (*update_func)(platform_window* window), void (*resize_func)(platform_window* window, u32 change_x,u32 change_y)) +{ + if (width < min_w) width = min_w; + if (height < min_h) width = min_h; + if (width > max_w) width = max_w; + if (height > max_h) width = max_h; + + log_assert(width > 0, "Width of window should be greater than 0"); + log_assert(height > 0, "Height of window should be greater than 0"); + log_assert(max_w >= 0, "Maximum width should be greater or equal to 0, where 0 means no limit"); + log_assert(max_h >= 0, "Maximum height should be greater or equal to 0, where 0 means no limit"); + log_assert(min_w > 0, "Minimum width should be greater than 0"); + log_assert(min_h > 0, "Minimum height should be greater than 0"); + log_assert(update_func, "Update function cannot be 0"); + + bool has_max_size = max_w || max_h; + + platform_window* window = mem_alloc(sizeof(platform_window)); + if (!window) return window; + window->width = width; + window->resize_func = resize_func; + window->height = height; + window->update_func = update_func; + window->backbuffer.buffer = 0; + window->gl_context = 0; + window->icon_loaded = false; + window->has_focus = true; + window->curr_cursor_type = CURSOR_DEFAULT; + window->next_cursor_type = CURSOR_DEFAULT; + window->clipboard_str = 0; + window->clipboard_strlen = 0; + window->do_draw = true; + window->backbuffer.s_image = 0; + + window->keyboard = keyboard_input_create(); + window->mouse = mouse_input_create(); + window->camera = (camera){0.0f,0.0f,0.0f}; + + static int att[] = + { + GLX_X_RENDERABLE , True, + GLX_DRAWABLE_TYPE , GLX_WINDOW_BIT, + GLX_RENDER_TYPE , GLX_RGBA_BIT, + GLX_X_VISUAL_TYPE , GLX_TRUE_COLOR, + GLX_RED_SIZE , 8, + GLX_GREEN_SIZE , 8, + GLX_BLUE_SIZE , 8, + GLX_ALPHA_SIZE , 8, + GLX_DEPTH_SIZE , 24, + GLX_STENCIL_SIZE , 8, + GLX_DOUBLEBUFFER , True, + //GLX_SAMPLE_BUFFERS , 1, + //GLX_SAMPLES , 4, + None + }; + + window->display = XOpenDisplay(NULL); + if (!window->display) goto exit_failure; + + XSetErrorHandler(_x11_error_handler); + + window->parent = DefaultRootWindow(window->display); + + int fbcount; + GLXFBConfig* fbc = IMP_glXChooseFBConfig(window->display, DefaultScreen(window->display), att, &fbcount); + if (!fbc) goto exit_failure; + int best_fbc = -1, worst_fbc = -1, best_num_samp = -1, worst_num_samp = 999; + + int i; + for (i=0; i<fbcount; ++i) + { + XVisualInfo *vi = IMP_glXGetVisualFromFBConfig(window->display, fbc[i] ); + if (vi) + { + int samp_buf, samples; + IMP_glXGetFBConfigAttrib(window->display, fbc[i], GLX_SAMPLE_BUFFERS, &samp_buf ); + IMP_glXGetFBConfigAttrib(window->display, fbc[i], GLX_SAMPLES , &samples ); + + if ( best_fbc < 0 || (samp_buf && samples > best_num_samp)) + best_fbc = i, best_num_samp = samples; + if ( worst_fbc < 0 || !samp_buf || samples < worst_num_samp ) + worst_fbc = i, worst_num_samp = samples; + } + XFree(vi); + } + + GLXFBConfig bestFbc = fbc[best_fbc]; + XFree(fbc); + + XVisualInfo *vi = IMP_glXGetVisualFromFBConfig(window->display, bestFbc ); + window->visual_info = vi; + if (!window->visual_info) goto exit_failure; + + window->cmap = XCreateColormap(window->display, window->parent, window->visual_info->visual, AllocNone); + + // calculate window center + XRRScreenResources *screens = IMP_XRRGetScreenResources(window->display, window->parent); + if (!screens) goto exit_failure; + XRRCrtcInfo *info = IMP_XRRGetCrtcInfo(window->display, screens, screens->crtcs[0]); + if (!info) goto exit_failure; + + s32 center_x = (info->width / 2) - (width / 2); + s32 center_y = (info->height / 2) - (height / 2); + + IMP_XRRFreeCrtcInfo(info); + IMP_XRRFreeScreenResources(screens); + + XSetWindowAttributes window_attributes; + window_attributes.colormap = window->cmap; + window_attributes.border_pixel = 0; + window_attributes.event_mask = KeyPressMask | KeyReleaseMask | PointerMotionMask | + ButtonPressMask | ButtonReleaseMask | StructureNotifyMask | FocusChangeMask | LeaveWindowMask; + + window->window = XCreateWindow(window->display, window->parent, center_x, center_y, width, height, 0, window->visual_info->depth, InputOutput, window->visual_info->visual, CWColormap | CWEventMask | CWBorderPixel, &window_attributes); + + + XGCValues values; + window->gc = XCreateGC(window->display, window->window, 0, &values); + + XMapWindow(window->display, window->window); + XFlush(window->display); + + XSync(window->display, False); + + XSizeHints hints; + + if (has_max_size) + hints.flags = PMaxSize | PMinSize | USPosition; + else + hints.flags = PMinSize | USPosition; + hints.x = center_x; + hints.y = center_y; + hints.max_width = max_w; + hints.max_height = max_h; + hints.min_width = min_w; + hints.min_height = min_h; + + XSetWMNormalHints(window->display, window->window, &hints); + + // window name + { + Atom WM_NAME = XInternAtom(window->display, "WM_NAME", False); + Atom _NET_WM_NAME = XInternAtom(window->display, "_NET_WM_NAME", False); + Atom _NET_WM_ICON_NAME = XInternAtom(window->display, "_NET_WM_ICON_NAME", False); + + char *list[1] = { (char *) name }; + XTextProperty property; + + XStoreName(window->display, window->window, name); + + Xutf8TextListToTextProperty(window->display, list, 1, XUTF8StringStyle, + &property); + XSetTextProperty(window->display, window->window, &property, WM_NAME); + XSetTextProperty(window->display, window->window, &property, _NET_WM_NAME); + XSetTextProperty(window->display, window->window, &property, XA_WM_NAME); + XSetTextProperty(window->display, window->window, &property, _NET_WM_ICON_NAME); + XFree(property.value); + + XClassHint class_hint; + class_hint.res_name = name; + class_hint.res_class = name; + XSetClassHint(window->display, window->window, &class_hint); + } + + + // hide taskbar icon and stay on top +#if 0 + { + Atom wm_state = XInternAtom(window->display, + "_NET_WM_STATE", False); + Atom taskbar_atom = XInternAtom(window->display, + "_NET_WM_STATE_SKIP_TASKBAR", False); + Atom above_atom = XInternAtom(window->display, + "_NET_WM_STATE_ABOVE", False); + + Atom atom_list[2] = {taskbar_atom, above_atom}; + XChangeProperty(window->display, window->window, wm_state, XA_ATOM, 32, + PropModeReplace, (const unsigned char*)&atom_list, 2); + } +#endif + + { + XWMHints* win_hints = XAllocWMHints(); + win_hints->flags = StateHint | IconPositionHint; + win_hints->initial_state = IconicState; + win_hints->icon_x = 0; + win_hints->icon_y = 0; + + /* pass the hints to the window manager. */ + XSetWMHints(window->display, window->window, win_hints); + XFree(win_hints); + } + + platform_setup_backbuffer(window); + platform_setup_renderer(); + + window->is_open = true; + window->width = width; + window->height = height; + + create_key_tables(window); + + // recieve window close event + window->quit = XInternAtom(window->display, "WM_DELETE_WINDOW", False); + + GET_ATOM(XdndEnter); + GET_ATOM(XdndPosition); + GET_ATOM(XdndStatus); + GET_ATOM(XdndTypeList); + GET_ATOM(XdndActionCopy); + GET_ATOM(XdndDrop); + GET_ATOM(XdndFinished); + GET_ATOM(XdndSelection); + GET_ATOM(XdndLeave); + GET_ATOM(PRIMARY); + GET_ATOM(CLIPBOARD); + GET_ATOM(UTF8_STRING); + GET_ATOM(COMPOUND_STRING); + GET_ATOM(TARGETS); + GET_ATOM(MULTIPLE); + GET_ATOM(_NET_WM_STATE); + + array atoms = array_create(sizeof(Atom)); + array_push(&atoms, &window->quit); + array_push(&atoms, &window->XdndEnter); + array_push(&atoms, &window->XdndPosition); + array_push(&atoms, &window->XdndStatus); + array_push(&atoms, &window->XdndTypeList); + array_push(&atoms, &window->XdndActionCopy); + array_push(&atoms, &window->XdndDrop); + array_push(&atoms, &window->XdndFinished); + array_push(&atoms, &window->XdndSelection); + array_push(&atoms, &window->XdndLeave); + array_push(&atoms, &window->PRIMARY); + array_push(&atoms, &window->CLIPBOARD); + array_push(&atoms, &window->UTF8_STRING); + array_push(&atoms, &window->COMPOUND_STRING); + array_push(&atoms, &window->TARGETS); + array_push(&atoms, &window->MULTIPLE); + array_push(&atoms, &window->_NET_WM_STATE); + + XSetWMProtocols(window->display, window->window, atoms.data, atoms.length); + array_destroy(&atoms); + + Atom XdndAware = XInternAtom(window->display, "XdndAware", False); + Atom xdnd_version = 5; + XChangeProperty(window->display, window->window, XdndAware, XA_ATOM, 32, + PropModeReplace, (unsigned char*)&xdnd_version, 1); + + + XFlush(window->display); + XSync(window->display, True); + + _platform_register_window(window); + + return window; + + exit_failure: + mem_free(window); + return 0; +} + +inline bool platform_window_is_valid(platform_window *window) +{ + return window && window->window && window->display; +} + +void platform_destroy_window(platform_window *window) +{ + if (platform_window_is_valid(window)) { + if (current_render_driver() == DRIVER_GL) + { + IMP_glXMakeCurrent(window->display, None, NULL); + IMP_glXDestroyContext(window->display, window->gl_context); + } + if (window->backbuffer.buffer) { mem_free(window->backbuffer.buffer); window->backbuffer.buffer = 0; } + + XDestroyWindow(window->display, window->window); + XCloseDisplay(window->display); + XFree(window->visual_info); + mem_free(window->clipboard_str); + _platform_unregister_window(window); + window->window = 0; + window->display = 0; + keyboard_input_destroy(&window->keyboard); + } +} + +void platform_hide_window_taskbar_icon(platform_window *window) +{ + XClientMessageEvent m; + memset(&m, 0, sizeof(XClientMessageEvent)); + m.type = ClientMessage; + m.display = window->display; + m.window = window->window; + m.message_type = window->_NET_WM_STATE; + m.format=32; + m.data.l[0] = 1; + m.data.l[1] = XInternAtom(window->display, "_NET_WM_STATE_SKIP_TASKBAR", False); + m.data.l[2] = None; + m.data.l[3] = 1; + m.data.l[4] = 0; + XSendEvent(window->display, window->window, False, SubstructureRedirectMask | SubstructureNotifyMask, (XEvent*)&m); + + XFlush(window->display); +} + +void _platform_handle_events_for_window(platform_window *window) +{ + mouse_input *mouse = &_global_mouse; + keyboard_input *keyboard = &_global_keyboard; + + mouse->left_state &= ~MOUSE_CLICK; + mouse->right_state &= ~MOUSE_CLICK; + mouse->left_state &= ~MOUSE_DOUBLE_CLICK; + mouse->right_state &= ~MOUSE_DOUBLE_CLICK; + mouse->left_state &= ~MOUSE_RELEASE; + mouse->right_state &= ~MOUSE_RELEASE; + mouse->is_hovering_item = false; + memset(keyboard->input_keys, 0, MAX_KEYCODE); + mouse->move_x = 0; + mouse->move_y = 0; + mouse->scroll_state = 0; + keyboard->text_changed = false; + window->do_draw = true; + + XClientMessageEvent m; + + s32 pending_events = XPending(window->display); + for (s32 i = 0; i < pending_events; i++) + { + XNextEvent(window->display, &window->event); + if (window->event.type == ClientMessage) + { + static int xdnd_version=0; + + if ((Atom)window->event.xclient.data.l[0] == window->quit) { + window->is_open = false; + } + + if (window->event.xclient.message_type == window->XdndDrop) + { + if (window->xdnd_req == None) { + /* say again - not interested! */ + memset(&m, 0, sizeof(XClientMessageEvent)); + m.type = ClientMessage; + m.display = window->event.xclient.display; + m.window = window->event.xclient.data.l[0]; + m.message_type = window->XdndFinished; + m.format=32; + m.data.l[0] = window->window; + m.data.l[1] = 0; + m.data.l[2] = None; /* fail! */ + XSendEvent(window->display, window->event.xclient.data.l[0], False, NoEventMask, (XEvent*)&m); + } else { + /* convert */ + if(xdnd_version >= 1) { + XConvertSelection(window->display, window->XdndSelection, window->xdnd_req, window->PRIMARY, window->window, window->event.xclient.data.l[2]); + } else { + printf("time to find the time.\n"); + //XConvertSelection(window->display, window->XdndSelection, window->xdnd_req, window->PRIMARY, window->xwindow, CurrentTime); + } + } + } + } + else if (window->event.type == LeaveNotify) + { + mouse->x = MOUSE_OFFSCREEN; + mouse->y = MOUSE_OFFSCREEN; + } + else if (window->event.type == ConfigureNotify) + { + XConfigureEvent xce = window->event.xconfigure; + window->width = xce.width; + window->height = xce.height; + + if (current_render_driver() == DRIVER_CPU) + _allocate_backbuffer(window); + else + IMP_glViewport(0, 0, window->width, window->height); + } + else if (window->event.type == FocusIn) + { + window->has_focus = true; + } + else if (window->event.type == FocusOut) + { + mouse->x = MOUSE_OFFSCREEN; + mouse->y = MOUSE_OFFSCREEN; + window->has_focus = false; + mouse->inside_of_window = (mouse->x >= 0 && mouse->y >= 0 && mouse->x < window->width && mouse->y < window->height); + + // if windows loses focus, set all keys to not pressed + memset(keyboard->keys, 0, MAX_KEYCODE); + } + else if (window->event.type == MotionNotify) + { + s32 x = mouse->x; + s32 y = mouse->y; + + mouse->total_move_x += window->event.xmotion.x - mouse->x; + mouse->total_move_y += window->event.xmotion.y - mouse->y; + + mouse->x = window->event.xmotion.x; + mouse->y = window->event.xmotion.y; + mouse->inside_of_window = (mouse->x >= 0 && mouse->y >= 0 && mouse->x < window->width && mouse->y < window->height); + + mouse->move_x = mouse->x - x; + mouse->move_y = mouse->y - y; + } + else if (window->event.type == ButtonPress) + { + Time ev_time = window->event.xbutton.time; + static Time last_ev_time = 0; + + bool is_left_down = window->event.xbutton.button == Button1; + bool is_right_down = window->event.xbutton.button == Button3; + //bool is_middle_down = window->event.xbutton.button == Button2; + bool scroll_up = window->event.xbutton.button == Button4; + bool scroll_down = window->event.xbutton.button == Button5; + + if (scroll_up) + mouse->scroll_state = SCROLL_UP; + if (scroll_down) + mouse->scroll_state = SCROLL_DOWN; + + if (is_left_down) + { + if (ev_time - last_ev_time < 200) + { + mouse->left_state |= MOUSE_DOUBLE_CLICK; + } + + mouse->left_state |= MOUSE_DOWN; + mouse->left_state |= MOUSE_CLICK; + + mouse->total_move_x = 0; + mouse->total_move_y = 0; + last_ev_time = ev_time; + } + if (is_right_down) + { + mouse->right_state |= MOUSE_DOWN; + mouse->right_state |= MOUSE_CLICK; + } + } + else if (window->event.type == ButtonRelease) + { + bool is_left_up = window->event.xbutton.button == Button1; + bool is_right_up = window->event.xbutton.button == Button3; + //bool is_middle_up = window->event.xbutton.button == Button2; + + if (is_left_up) + { + mouse->left_state = MOUSE_RELEASE; + } + if (is_right_up) + { + mouse->right_state = MOUSE_RELEASE; + } + } + else if(window->event.type == KeyPress) + { + s32 key = window->event.xkey.keycode; + + keyboard->keys[keycode_map[key]] = true; + keyboard->input_keys[keycode_map[key]] = true; + + // https://gist.github.com/rickyzhang82/8581a762c9f9fc6ddb8390872552c250 + //printf("state: %d\n", window->event.xkey.state); + + // remove key control key from mask so it doesnt block input + window->event.xkey.state &= ~ControlMask; + + // replace capslock with shiftkey else keylookup returns 0... + if (window->event.xkey.state == 2) + window->event.xkey.state = 1; + + KeySym ksym = XLookupKeysym(&window->event.xkey, window->event.xkey.state); + + if (keyboard->take_input) + { + char *ch = 0; + switch(ksym) + { + case XK_space: ch = " "; break; + case XK_exclam: ch = "!"; break; + case XK_quotedbl: ch = "\""; break; + case XK_numbersign: ch = "#"; break; + case XK_dollar: ch = "$"; break; + case XK_percent: ch = "%"; break; + case XK_ampersand: ch = "&"; break; + case XK_apostrophe: ch = "`"; break; + case XK_parenleft: ch = "("; break; + case XK_parenright: ch = ")"; break; + case XK_asterisk: ch = "*"; break; + case XK_plus: ch = "+"; break; + case XK_comma: ch = ","; break; + case XK_minus: ch = "-"; break; + case XK_period: ch = "."; break; + case XK_slash: ch = "/"; break; + case XK_0: ch = "0"; break; + case XK_1: ch = "1"; break; + case XK_2: ch = "2"; break; + case XK_3: ch = "3"; break; + case XK_4: ch = "4"; break; + case XK_5: ch = "5"; break; + case XK_6: ch = "6"; break; + case XK_7: ch = "7"; break; + case XK_8: ch = "8"; break; + case XK_9: ch = "9"; break; + + case XK_colon: ch = ":"; break; + case XK_semicolon: ch = ";"; break; + case XK_less: ch = "<"; break; + case XK_equal: ch = "="; break; + case XK_greater: ch = ">"; break; + case XK_question: ch = "?"; break; + case XK_at: ch = "@"; break; + case XK_bracketleft: ch = "["; break; + case XK_backslash: ch = "\\"; break; + case XK_bracketright: ch = "]"; break; + case XK_asciicircum: ch = "^"; break; + case XK_underscore: ch = "_"; break; + case XK_grave: ch = "`"; break; + case XK_braceleft: ch = "{"; break; + case XK_bar: ch = "|"; break; + case XK_braceright: ch = "}"; break; + case XK_asciitilde: ch = "~"; break; + } + + if ((ksym >= XK_A && ksym <= XK_Z) || (ksym >= XK_a && ksym <= XK_z)) + { + ch = XKeysymToString(ksym); + } + + if (ch && keyboard->input_mode == INPUT_NUMERIC) + { + if (!(*ch >= 48 && *ch <= 57)) + { + ch = 0; + } + } + + keyboard_handle_input_string(window, ch); + } + } + else if (window->event.type == KeyRelease) + { + s32 key = window->event.xkey.keycode; + keyboard->keys[keycode_map[key]] = false; + + //KeySym ksym = XLookupKeysym(&window->event.xkey, 0); + } + else if (window->event.type == SelectionClear) + { + window->clipboard_str = 0; + window->clipboard_strlen = 0; + } + else if (window->event.type == SelectionRequest) + { + Atom formats[] = {window->UTF8_STRING, window->COMPOUND_STRING, XA_STRING}; + Atom targets[] = {window->TARGETS, window->MULTIPLE, window->UTF8_STRING, window->COMPOUND_STRING, XA_STRING}; + int formatCount = sizeof(formats) / sizeof(formats[0]); + + XSelectionEvent event = {.type = SelectionNotify, .selection = window->event.xselectionrequest.selection, .target = window->event.xselectionrequest.target, .display = window->event.xselectionrequest.display, .requestor = window->event.xselectionrequest.requestor, .time = window->event.xselectionrequest.time}; + + if(window->event.xselectionrequest.target == window->TARGETS) { + XChangeProperty(window->display, window->event.xselectionrequest.requestor, window->event.xselectionrequest.property, XA_ATOM, 32, PropModeReplace, (unsigned char*)targets, sizeof(targets) / sizeof(targets[0])); + + event.property = window->event.xselectionrequest.property; + } else { + event.property = None; + int i; + for(i = 0; i < formatCount; i++) { + if(window->event.xselectionrequest.target == formats[i]) { + XChangeProperty(window->display, window->event.xselectionrequest.requestor, window->event.xselectionrequest.property, window->event.xselectionrequest.target, 8, PropModeReplace, (unsigned char*)window->clipboard_str, window->clipboard_strlen); + + event.property = window->event.xselectionrequest.property; + break; + } + } + } + + XSendEvent(window->display, window->event.xselectionrequest.requestor, False, 0, (XEvent*)&event); + XFlush(window->display); + } + } +} + +inline void platform_show_alert(char *title, char *message) +{ + char command[MAX_INPUT_LENGTH]; + snprintf(command, MAX_INPUT_LENGTH, "notify-send \"%s\" \"%s\"", title, message); + platform_run_command(command); +} + +inline void platform_window_swap_buffers(platform_window *window) +{ + window->do_draw = false; + + // set cursor if changed + if (window->curr_cursor_type != window->next_cursor_type) + { + int cursor_shape = 0; + switch(window->next_cursor_type) + { + case CURSOR_DEFAULT: cursor_shape = XC_arrow; break; + case CURSOR_POINTER: cursor_shape = XC_hand1; break; + case CURSOR_LOADING: cursor_shape = XC_watch; break; + case CURSOR_DRAG: cursor_shape = XC_sb_h_double_arrow; break; + case CURSOR_TEXT: cursor_shape = XC_xterm; break; + } + Cursor cursor = XCreateFontCursor(window->display, cursor_shape); + XDefineCursor(window->display, window->window, cursor); + window->curr_cursor_type = window->next_cursor_type; + } + + if (current_render_driver() == DRIVER_CPU) + { + s32 pixel_count = window->backbuffer.width * window->backbuffer.height; + for (s32 i = 0; i < pixel_count; i++) + { + u8 *buffer_entry = window->backbuffer.buffer + (i*5); + memcpy(window->backbuffer.buffer + (i*4), buffer_entry, 4); + } + + XPutImage(window->display, window->window, window->gc, window->backbuffer.s_image, 0, 0, 0, 0, window->backbuffer.width, window->backbuffer.height); + XFlush(window->display); + } + else + { + IMP_glXSwapBuffers(window->display, window->window); + } +} + +void platform_toggle_vsync(platform_window* window, bool on) +{ + // placeholder. +} + +u64 platform_get_time(time_type time_type, time_precision precision) +{ + s32 type = CLOCK_REALTIME; + switch(time_type) + { + case TIME_FULL: type = CLOCK_REALTIME; break; + case TIME_THREAD: type = CLOCK_THREAD_CPUTIME_ID; break; + case TIME_PROCESS: type = CLOCK_PROCESS_CPUTIME_ID; break; + } + + struct timespec tms; + if (clock_gettime(type,&tms)) { + return -1; + } + + long result = 0; + + if (precision == TIME_NS) + { + result = tms.tv_sec * 1000000000; + result += tms.tv_nsec; + if (tms.tv_nsec % 1000 >= 500) { + ++result; + } + } + else if (precision == TIME_US) + { + result = tms.tv_sec * 1000000; + result += tms.tv_nsec/1000; + if (tms.tv_nsec % 1000 >= 500) { + ++result; + } + } + else if (precision == TIME_MILI_S) + { + result = tms.tv_sec * 1000; + result += tms.tv_nsec/1000000; + if (tms.tv_nsec % 1000 >= 500) { + ++result; + } + } + else if (precision == TIME_S) + { + result = tms.tv_sec; + result += tms.tv_nsec/1000000000; + if (tms.tv_nsec % 1000 >= 500) { + ++result; + } + } + + return result; +} + +inline s32 platform_get_cpu_count() +{ + return (int)sysconf(_SC_NPROCESSORS_ONLN); +} + +inline s32 platform_get_memory_size() +{ + uint64_t aid = (uint64_t) sysconf(_SC_PHYS_PAGES); + aid *= (uint64_t) sysconf(_SC_PAGESIZE); + aid /= (uint64_t) (1024 * 1024); + return (int)(aid); +} + +void platform_show_message(platform_window *window, char *message, char *title) +{ + char command[MAX_INPUT_LENGTH]; + snprintf(command, MAX_INPUT_LENGTH, "zenity --info --text=\"%s\" --title=\"%s\" --width=240", message, title); + popen(command, "r"); +} + +static void* platform_open_file_dialog_thread(void *data) +{ + open_dialog_args *args = data; + + FILE *f; + + char current_val[MAX_INPUT_LENGTH]; + string_copyn(current_val, args->buffer, MAX_INPUT_LENGTH); + + char file_filter[30]; + file_filter[0] = 0; + if (args->file_filter) + snprintf(file_filter, 30, "--file-filter=\"%s\"", args->file_filter); + + char start_path[30]; + start_path[0] = 0; + if (args->start_path) + snprintf(start_path, 30, "--filename=\"%s\"", args->start_path); + + + u16 command_size = MAX_INPUT_LENGTH + 200; + char command[command_size]; + + if (args->type == OPEN_FILE) + { + snprintf(command, command_size, "zenity --file-selection %s %s", file_filter, start_path); + } + else if (args->type == OPEN_DIRECTORY) + { + snprintf(command, command_size, "zenity --file-selection --directory %s %s", file_filter, start_path); + } + else if (args->type == SAVE_FILE) + { + snprintf(command, command_size, "zenity --file-selection --save --confirm-overwrite %s %s", file_filter, start_path); + } + + f = popen(command, "r"); + + char buffer[MAX_INPUT_LENGTH]; + char *result = fgets(buffer, MAX_INPUT_LENGTH, f); + + if (!result) + return 0; + + // replace newlines with 0, we only want one file path + s32 len = strlen(buffer); + for (s32 x = 0; x < len; x++) + { + if (buffer[x] == '\n') buffer[x] = 0; + } + + if (strcmp(buffer, current_val) != 0 && strcmp(buffer, "") != 0) + { + string_copyn(args->buffer, buffer, MAX_INPUT_LENGTH); + s32 len = strlen(args->buffer); + args->buffer[len] = 0; + } + + return 0; +} + +void *platform_open_file_dialog_block(void *arg) +{ + thread thr = thread_start(platform_open_file_dialog_thread, arg); + thread_join(&thr); + mem_free(arg); + return 0; +} + +void platform_list_files_block(array *list, char *start_dir, array filters, bool recursive, memory_bucket *bucket, bool include_directories, bool *is_cancelled, search_info *info) +{ + log_assert(list, "List is null"); + + s32 len = 0; + char *matched_filter = 0; + + char *subdirname_buf; + if (bucket) + subdirname_buf = memory_bucket_reserve(bucket, MAX_INPUT_LENGTH); + else + subdirname_buf = mem_alloc(MAX_INPUT_LENGTH); + + DIR *d; + struct dirent *dir; + d = opendir(start_dir); + if (d) { + platform_set_active_directory(start_dir); + while ((dir = readdir(d)) != NULL) { + if (*is_cancelled) break; + platform_set_active_directory(start_dir); + + if (dir->d_type == DT_DIR) + { + if ((strcmp(dir->d_name, ".") == 0) || (strcmp(dir->d_name, "..") == 0)) + continue; + + if (include_directories) + { + if ((len = platform_filter_matches(&filters, dir->d_name, + &matched_filter)) && len != -1) + { + char *buf; + if (bucket) + buf = memory_bucket_reserve(bucket, MAX_INPUT_LENGTH); + else + buf = mem_alloc(MAX_INPUT_LENGTH); + + //realpath(dir->d_name, buf); + snprintf(buf, MAX_INPUT_LENGTH, "%s%s",start_dir, dir->d_name); + + found_file f; + f.path = buf; + + if (bucket) + f.matched_filter = memory_bucket_reserve(bucket, len+1); + else + f.matched_filter = mem_alloc(len+1); + + string_copyn(f.matched_filter, matched_filter, len+1); + + mutex_lock(&list->mutex); + array_push_size(list, &f, sizeof(found_file)); + mutex_unlock(&list->mutex); + } + } + + if (recursive) + { + if (info) info->dir_count++; + + string_copyn(subdirname_buf, start_dir, MAX_INPUT_LENGTH); + string_appendn(subdirname_buf, dir->d_name, MAX_INPUT_LENGTH); + string_appendn(subdirname_buf, "/", MAX_INPUT_LENGTH); + + // do recursive search + platform_list_files_block(list, subdirname_buf, filters, recursive, bucket, include_directories, is_cancelled, info); + } + } + // we handle DT_UNKNOWN for file systems that do not support type lookup. + else if (dir->d_type == DT_REG || dir->d_type == DT_UNKNOWN) + { + if (info) info->file_count++; + + // check if name matches pattern + if ((len = platform_filter_matches(&filters, dir->d_name, + &matched_filter)) && len != -1) + { + char *buf; + if (bucket) + buf = memory_bucket_reserve(bucket, MAX_INPUT_LENGTH); + else + buf = mem_alloc(MAX_INPUT_LENGTH); + + //realpath(dir->d_name, buf); + snprintf(buf, MAX_INPUT_LENGTH, "%s%s",start_dir, dir->d_name); + + found_file f; + f.path = buf; + + if (bucket) + f.matched_filter = memory_bucket_reserve(bucket, len+1); + else + f.matched_filter = mem_alloc(len+1); + + string_copyn(f.matched_filter, matched_filter, len+1); + + mutex_lock(&list->mutex); + array_push_size(list, &f, sizeof(found_file)); + mutex_unlock(&list->mutex); + + } + } + } + closedir(d); + } + + if (!bucket) + mem_free(subdirname_buf); +} + +char *platform_get_full_path(char *file) +{ + char *buf = mem_alloc(PATH_MAX); + buf[0] = 0; + + char *result = realpath(file, buf); + + if (!result) + { + buf[0] = 0; + return buf; + } + + return buf; +} + +inline void platform_open_url(char *url) +{ + char buffer[MAX_INPUT_LENGTH]; + snprintf(buffer, MAX_INPUT_LENGTH, "xdg-open %s", url); + platform_run_command(buffer); +} + +inline void platform_run_command(char *command) +{ + system(command); +} + +void platform_set_icon(platform_window *window, image *img) +{ + if (!img->loaded) return; + if (!window->icon_loaded) + window->icon_loaded = true; + else + return; + + s32 w = img->width; + s32 h = img->height; + + s32 nelements = (w * h) + 2; + + unsigned long data[nelements]; + int i = 0; + (data)[i++] = w; + (data)[i++] = h; + + for (s32 y = 0; y < h; y++) + { + for (s32 x = 0; x < w; x++) + { + s32 *pixel = (s32*)(&((data)[i++])); + + s32 img_pixel = *(((s32*)img->data+(x+(y*w)))); + +#if 0 + // 0xAABBGGRR + s32 a = (img_pixel>>24) & 0x000000FF; + s32 b = (img_pixel>>16) & 0x000000FF; + s32 g = (img_pixel>> 8) & 0x000000FF; + s32 r = (img_pixel>> 0) & 0x000000FF; + + //s32 c = (r << 24) | (g << 16) | (b << 8) | (a << 0); + s32 c = (a << 24) | (r << 16) | (g << 8) | (b << 0); +#endif + s32 c = img_pixel; + *pixel = c; + } + } + + Atom property = XInternAtom(window->display, "_NET_WM_ICON", 0); + Atom cardinal = XInternAtom(window->display, "CARDINAL", False); + + XChangeProperty(window->display, window->window, + property, cardinal, 32, PropModeReplace, + (unsigned char *)data, nelements); +} + +#if 0 +uint write_cb(char *in, uint size, uint nmemb, char *buffer) +{ + string_appendn(buffer, in, MAX_INPUT_LENGTH); + return size * nmemb; +} + +bool platform_send_http_request(char *url, char *params, char *response_buffer) +{ + string_copyn(response_buffer, "", MAX_INPUT_LENGTH); + + char fullurl[200]; + sprintf(fullurl, "https://%s/%s", url, params); + curl_easy_setopt(curl, CURLOPT_URL,fullurl); + curl_easy_setopt(curl, CURLOPT_SSL_VERIFYPEER, 0L); + curl_easy_setopt(curl, CURLOPT_SSL_VERIFYHOST, 0L); + curl_easy_setopt(curl, CURLOPT_NOPROGRESS, 1L); + curl_easy_setopt(curl, CURLOPT_VERBOSE, 0L); + curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, write_cb); + curl_easy_setopt(curl, CURLOPT_WRITEDATA, response_buffer); + CURLcode res = curl_easy_perform(curl); + if (res != CURLE_OK) return false; + + return true; +} + +bool platform_get_mac_address(char *buffer, s32 buf_size) +{ + struct ifaddrs *ifaddr=NULL; + struct ifaddrs *ifa = NULL; + int i = 0; + + if (getifaddrs(&ifaddr) == -1) + { + return false; + } + else + { + for ( ifa = ifaddr; ifa != NULL; ifa = ifa->ifa_next) + { + if ( (ifa->ifa_addr) && (ifa->ifa_addr->sa_family == AF_PACKET) ) + { + struct sockaddr_ll *s = (struct sockaddr_ll*)ifa->ifa_addr; + for (i=0; i <s->sll_halen; i++) + { + buffer += sprintf(buffer, "%02x", (s->sll_addr[i])); + + if (i+1!=s->sll_halen) + { + buffer += sprintf(buffer, "%c", '-'); + } + } + + freeifaddrs(ifaddr); + return true; + } + } + } + + return false; +} +#endif
\ No newline at end of file |
