From 0422dadbc8832816f626c07ae3e2ea335f3a478c Mon Sep 17 00:00:00 2001 From: Allen Webster Date: Wed, 19 Feb 2020 21:32:07 -0800 Subject: [PATCH] Ohh git --- custom/4coder_custom.cpp | 1 + custom/bin/buildsuper_x64-linux.sh | 2 +- platform_linux/linux_4ed.cpp | 675 +++++++++++++++-------------- 3 files changed, 340 insertions(+), 338 deletions(-) diff --git a/custom/4coder_custom.cpp b/custom/4coder_custom.cpp index 3dda1c74..976c10da 100644 --- a/custom/4coder_custom.cpp +++ b/custom/4coder_custom.cpp @@ -6,6 +6,7 @@ extern "C" b32 get_version(i32 maj, i32 min, i32 patch){ + printf("CUS: %d.%d.%d\n", MAJOR, MINOR, PATCH); return(maj == MAJOR && min == MINOR && patch == PATCH); } diff --git a/custom/bin/buildsuper_x64-linux.sh b/custom/bin/buildsuper_x64-linux.sh index 51a3051b..7b975b16 100644 --- a/custom/bin/buildsuper_x64-linux.sh +++ b/custom/bin/buildsuper_x64-linux.sh @@ -21,7 +21,7 @@ arch=-m64 preproc_file=4coder_command_metadata.i meta_macros="-DMETA_PASS" g++ -I"$CODE_HOME" $meta_macros $arch $opts $debug -std=c++11 "$SOURCE" -E -o $preproc_file -g++ -I"$CODE_HOME" $opts $debug -std=gnu++0x "$CODE_HOME/4coder_metadata_generator.cpp" -o "$CODE_HOME/metadata_generator" +g++ -I"$CODE_HOME" $opts $debug -std=c++11 "$CODE_HOME/4coder_metadata_generator.cpp" -o "$CODE_HOME/metadata_generator" "$CODE_HOME/metadata_generator" -R "$CODE_HOME" "$PWD/$preproc_file" g++ -I"$CODE_HOME" $arch $opts $debug -std=gnu++0x "$SOURCE" -shared -o custom_4coder.so -fPIC diff --git a/platform_linux/linux_4ed.cpp b/platform_linux/linux_4ed.cpp index 66444b7f..e34176d1 100644 --- a/platform_linux/linux_4ed.cpp +++ b/platform_linux/linux_4ed.cpp @@ -101,18 +101,18 @@ #include #ifdef INSO_DEBUG - #define LINUX_FN_DEBUG(fmt, ...) do { \ - fprintf(stderr, "%s: " fmt "\n", __func__, ##__VA_ARGS__);\ - } while (0) +#define LINUX_FN_DEBUG(fmt, ...) do { \ +fprintf(stderr, "%s: " fmt "\n", __func__, ##__VA_ARGS__);\ +} while (0) // I want to see a message - #undef AssertBreak - #define AssertBreak(m) ({\ - fprintf(stderr, "\n** ASSERTION FAILURE: %s:%d: %s\n\n", __FILE__, __LINE__, #m);\ +#undef AssertBreak +#define AssertBreak(m) ({\ +fprintf(stderr, "\n** ASSERTION FAILURE: %s:%d: %s\n\n", __FILE__, __LINE__, #m);\ *((volatile u64*)0) = 0xba771e70ad5;\ - }) +}) #else - #define LINUX_FN_DEBUG(...) +#define LINUX_FN_DEBUG(...) #endif //////////////////////////// @@ -150,46 +150,46 @@ struct Linux_Memory_Tracker_Node { struct Linux_Vars { Thread_Context tctx; Arena *frame_arena; - + Display* dpy; Window win; - + b32 has_xfixes; int xfixes_selection_event; XIM xim; XIC xic; FcConfig* fontconfig; XkbDescPtr xkb; - + Linux_Input_Chunk input; int xkb_event; int xkb_group; // active keyboard layout (0-3) - + int epoll; int step_timer_fd; u64 last_step_time; b32 step_pending; - + XCursor xcursors[APP_MOUSE_CURSOR_COUNT]; Application_Mouse_Cursor cursor; XCursor hidden_cursor; i32 cursor_show; i32 prev_cursor_show; - + Node free_linux_objects; Node timer_objects; - + System_Mutex global_frame_mutex; pthread_mutex_t memory_tracker_mutex; Linux_Memory_Tracker_Node* memory_tracker_head; Linux_Memory_Tracker_Node* memory_tracker_tail; int memory_tracker_count; - + Arena* clipboard_arena; String_Const_u8 clipboard_contents; b32 received_new_clipboard; b32 clipboard_catch_all; - + Atom atom_TARGETS; Atom atom_CLIPBOARD; Atom atom_UTF8_STRING; @@ -267,19 +267,19 @@ handle_to_object(Plat_Handle ph){ internal Linux_Object* linux_alloc_object(Linux_Object_Kind kind){ Linux_Object* result = NULL; - + if (linuxvars.free_linux_objects.next != &linuxvars.free_linux_objects) { result = CastFromMember(Linux_Object, node, linuxvars.free_linux_objects.next); } - + if (result == NULL) { i32 count = 512; - + Linux_Object* objects = (Linux_Object*)system_memory_allocate( - sizeof(Linux_Object) * count, - file_name_line_number_lit_u8 - ); - + sizeof(Linux_Object) * count, + file_name_line_number_lit_u8 + ); + objects[0].node.prev = &linuxvars.free_linux_objects; linuxvars.free_linux_objects.next = &objects[0].node; for (i32 i = 1; i < count; ++i) { @@ -288,10 +288,10 @@ linux_alloc_object(Linux_Object_Kind kind){ } objects[count - 1].node.next = &linuxvars.free_linux_objects; linuxvars.free_linux_objects.prev = &objects[count - 1].node; - + result = CastFromMember(Linux_Object, node, linuxvars.free_linux_objects.next); } - + Assert(result != 0); dll_remove(&result->node); block_zero_struct(result); @@ -313,12 +313,12 @@ internal int linux_compare_file_infos(File_Info** a, File_Info** b) { b32 a_hidden = (*a)->file_name.str[0] == '.'; b32 b_hidden = (*b)->file_name.str[0] == '.'; - + // hidden files lower in list if(a_hidden != b_hidden) { return a_hidden - b_hidden; } - + // push_stringf seems to null terminate return strcoll((char*)(*a)->file_name.str, (char*)(*b)->file_name.str); } @@ -361,18 +361,18 @@ linux_schedule_step(){ if(!__sync_bool_compare_and_swap(&linuxvars.step_pending, 0, 1)) { return; } - + u64 now = system_now_time(); u64 diff = (now - linuxvars.last_step_time); - + struct itimerspec its = {}; - + if(diff >= frame_nseconds) { its.it_value.tv_nsec = 1; } else { its.it_value.tv_nsec = frame_nseconds - diff; } - + timerfd_settime(linuxvars.step_timer_fd, 0, &its, NULL); } @@ -385,7 +385,7 @@ enum wm_state_mode { internal void linux_set_wm_state(Atom one, Atom two, enum wm_state_mode mode){ //NOTE(inso): this will only work after the window has been mapped - + XEvent e = {}; e.xany.type = ClientMessage; e.xclient.message_type = linuxvars.atom__NET_WM_STATE; @@ -395,7 +395,7 @@ linux_set_wm_state(Atom one, Atom two, enum wm_state_mode mode){ e.xclient.data.l[1] = one; e.xclient.data.l[2] = two; e.xclient.data.l[3] = 1L; - + XSendEvent(linuxvars.dpy, RootWindow(linuxvars.dpy, 0), 0, SubstructureNotifyMask | SubstructureRedirectMask, &e); @@ -419,71 +419,71 @@ linux_get_xsettings_dpi(Display* dpy, int screen){ u16 name_len; char name[0]; }; - + struct XSettings { u8 byte_order; u8 pad[3]; u32 serial; u32 num_settings; }; - + enum { XSettingsTypeInt, XSettingsTypeString, XSettingsTypeColor }; - + int dpi = -1; unsigned char* prop = NULL; char sel_buffer[64]; struct XSettings* xs; const char* p; - + snprintf(sel_buffer, sizeof(sel_buffer), "_XSETTINGS_S%d", screen); - + Atom XSET_SEL = XInternAtom(dpy, sel_buffer, True); Atom XSET_SET = XInternAtom(dpy, "_XSETTINGS_SETTINGS", True); - + if (XSET_SEL == None || XSET_SET == None){ //LOG("XSETTINGS unavailable.\n"); return(dpi); } - + Window xset_win = XGetSelectionOwner(dpy, XSET_SEL); if (xset_win == None){ // TODO(inso): listen for the ClientMessage about it becoming available? - // there's not much point atm if DPI scaling is only done at startup + // there's not much point atm if DPI scaling is only done at startup goto out; } - + { Atom type; int fmt; unsigned long pad, num; - + if (XGetWindowProperty(dpy, xset_win, XSET_SET, 0, 1024, False, XSET_SET, &type, &fmt, &num, &pad, &prop) != Success){ //LOG("XSETTINGS: GetWindowProperty failed.\n"); goto out; } - + if (fmt != 8){ //LOG("XSETTINGS: Wrong format.\n"); goto out; } } - + xs = (struct XSettings*)prop; p = (char*)(xs + 1); - + if (xs->byte_order != 0){ //LOG("FIXME: XSETTINGS not host byte order?\n"); goto out; } - + for (int i = 0; i < xs->num_settings; ++i){ struct XSettingHeader* h = (struct XSettingHeader*)p; - + p += sizeof(struct XSettingHeader); p += h->name_len; p += ((4 - (h->name_len & 3)) & 3); p += 4; // serial - + switch (h->type){ case XSettingsTypeInt: { if (strncmp(h->name, "Xft/DPI", h->name_len) == 0){ @@ -492,30 +492,30 @@ linux_get_xsettings_dpi(Display* dpy, int screen){ } p += 4; } break; - + case XSettingsTypeString: { u32 len = *(u32*)p; p += 4; p += len; p += ((4 - (len & 3)) & 3); } break; - + case XSettingsTypeColor: { p += 8; } break; - + default: { //LOG("XSETTINGS: Got invalid type...\n"); goto out; } break; } } - + out: - if (prop){ - XFree(prop); - } - + if (prop){ + XFree(prop); + } + return dpi; } @@ -563,15 +563,15 @@ graphics_fill_texture_sig(){ internal Face_Description linux_find_font(Face_Description* desc) { Face_Description result = *desc; - + char* name = strndupa((char*)desc->font.file_name.str, desc->font.file_name.size); - + double size; const char* style; { Face_Load_Parameters* p = &desc->parameters; size = p->pt_size; - + if(p->bold && p->italic) { style = "Bold Italic"; } else if(p->bold) { @@ -582,59 +582,59 @@ linux_find_font(Face_Description* desc) { style = "Regular"; } } - + FcPattern *pattern = FcPatternBuild( - 0, - FC_POSTSCRIPT_NAME, FcTypeString, name, - FC_SIZE, FcTypeDouble, size, - FC_FONTFORMAT, FcTypeString, "TrueType", - FC_STYLE, FcTypeString, (FcChar8*)style, - NULL); - + 0, + FC_POSTSCRIPT_NAME, FcTypeString, name, + FC_SIZE, FcTypeDouble, size, + FC_FONTFORMAT, FcTypeString, "TrueType", + FC_STYLE, FcTypeString, (FcChar8*)style, + NULL); + if(!pattern) { return result; } - + if (FcConfigSubstitute(linuxvars.fontconfig, pattern, FcMatchPattern)){ FcDefaultSubstitute(pattern); - + FcResult res; FcPattern *font = FcFontMatch(linuxvars.fontconfig, pattern, &res); if (!font){ return result; } - + FcChar8 *filename = 0; FcPatternGetString(font, FC_FILE, 0, &filename); if(filename) { LINUX_FN_DEBUG("FONTCONFIG FILENAME = %s\n", filename); result.font.file_name = push_u8_stringf(linuxvars.frame_arena, "%s", filename); } - + FcPatternDestroy(font); } - + FcPatternDestroy(pattern); - + return result; } internal font_make_face_sig() { - + Face* result = ft__font_make_face(arena, description, scale_factor); - + // if it failed to load the font directly, try via fontconfig. if(!result) { Face_Description desc2 = {}; desc2.parameters = description->parameters; desc2.font.file_name = string_front_of_path(description->font.file_name); - + printf("FONT %.*s\n", string_expand(desc2.font.file_name)); desc2 = linux_find_font(&desc2); result = ft__font_make_face(arena, &desc2, scale_factor); } - + if(!result) { // is this fatal? 4ed.cpp:277 (caller) does not check for null. String_Const_u8 s = description->font.file_name; @@ -642,7 +642,7 @@ font_make_face_sig() { snprintf(msg, sizeof(msg), "Unable to load font: %.*s", (int)s.size, s.str); system_error_box(msg); } - + return(result); } @@ -651,17 +651,17 @@ font_make_face_sig() { internal b32 glx_init(void) { int glx_maj, glx_min; - + if(!glXQueryVersion(linuxvars.dpy, &glx_maj, &glx_min)) { return false; } - + return glx_maj > 1 || (glx_maj == 1 && glx_min >= 3); } internal b32 glx_get_config(GLXFBConfig* fb_config, XVisualInfo* vi) { - + static const int attrs[] = { GLX_X_RENDERABLE , True, GLX_DRAWABLE_TYPE, GLX_WINDOW_BIT, @@ -676,24 +676,24 @@ glx_get_config(GLXFBConfig* fb_config, XVisualInfo* vi) { GLX_DOUBLEBUFFER , True, None }; - + int conf_count = 0; GLXFBConfig* conf_list = glXChooseFBConfig(linuxvars.dpy, DefaultScreen(linuxvars.dpy), attrs, &conf_count); if(!conf_count || conf_count <= 0) { return false; } - + *fb_config = *conf_list; XFree(conf_list); - + XVisualInfo* xvi = glXGetVisualFromFBConfig(linuxvars.dpy, *fb_config); if(!xvi) { return false; } - + *vi = *xvi; XFree(xvi); - + return true; } @@ -714,19 +714,19 @@ typedef int (glXSwapIntervalSGI_Function) (int interval); internal b32 glx_create_context(GLXFBConfig fb_config){ const char *glx_exts = glXQueryExtensionsString(linuxvars.dpy, DefaultScreen(linuxvars.dpy)); - + glXCreateContextAttribsARB_Function *glXCreateContextAttribsARB = 0; glXSwapIntervalEXT_Function *glXSwapIntervalEXT = 0; glXSwapIntervalMESA_Function *glXSwapIntervalMESA = 0; glXGetSwapIntervalMESA_Function *glXGetSwapIntervalMESA = 0; glXSwapIntervalSGI_Function *glXSwapIntervalSGI = 0; - + #define GLXLOAD(f) f = (f##_Function*) glXGetProcAddressARB((const GLubyte*) #f); GLXLOAD(glXCreateContextAttribsARB); - + GLXContext ctx = NULL; int (*old_handler)(Display*, XErrorEvent*) = XSetErrorHandler(&glx_error_handler); - + if (glXCreateContextAttribsARB == NULL){ //LOG("glXCreateContextAttribsARB() not found, using old-style GLX context\n" ); ctx = glXCreateNewContext(linuxvars.dpy, fb_config, GLX_RGBA_TYPE, 0, True); @@ -740,32 +740,32 @@ glx_create_context(GLXFBConfig fb_config){ #endif None }; - + //LOG("Creating GL 2.1 context... "); ctx = glXCreateContextAttribsARB(linuxvars.dpy, fb_config, 0, True, context_attribs); } - + XSync(linuxvars.dpy, False); if(glx_ctx_error || !ctx) { return false; } - + XSync(linuxvars.dpy, False); XSetErrorHandler(old_handler); - + //b32 direct = glXIsDirect(linuxvars.dpy, ctx); - + //LOG("Making context current\n"); glXMakeCurrent(linuxvars.dpy, linuxvars.win, ctx); - + //glx_enable_vsync(); - + // NOTE(allen): Load gl functions #define GL_FUNC(f,R,P) GLXLOAD(f) #include "opengl/4ed_opengl_funcs.h" - + #undef GLXLOAD - + return true; } @@ -773,17 +773,17 @@ glx_create_context(GLXFBConfig fb_config){ internal void linux_x11_init(int argc, char** argv, Plat_Settings* settings) { - + Display* dpy = XOpenDisplay(0); if (!dpy){ fprintf(stderr, "FATAL: Cannot open X11 Display!\n"); exit(1); } - + linuxvars.dpy = dpy; - + #define LOAD_ATOM(x) linuxvars.atom_##x = XInternAtom(linuxvars.dpy, #x, False); - + LOAD_ATOM(TARGETS); LOAD_ATOM(CLIPBOARD); LOAD_ATOM(UTF8_STRING); @@ -796,140 +796,140 @@ linux_x11_init(int argc, char** argv, Plat_Settings* settings) { LOAD_ATOM(_NET_WM_WINDOW_TYPE_NORMAL); LOAD_ATOM(_NET_WM_PID); LOAD_ATOM(WM_DELETE_WINDOW); - + #undef LOAD_ATOM - + if (!glx_init()){ system_error_box("Your XServer's GLX version is too old. GLX 1.3+ is required."); } - + GLXFBConfig fb_config; XVisualInfo vi; if (!glx_get_config(&fb_config, &vi)){ system_error_box("Could not get a matching GLX FBConfig. Check your OpenGL drivers are installed correctly."); } - + // TODO: window size #define WINDOW_W_DEFAULT 800 #define WINDOW_H_DEFAULT 600 int w = WINDOW_W_DEFAULT; int h = WINDOW_H_DEFAULT; - + // TEMP render_target.width = w; render_target.height = h; - + XSetWindowAttributes swa = {}; swa.backing_store = WhenMapped; swa.event_mask = StructureNotifyMask; swa.bit_gravity = NorthWestGravity; swa.colormap = XCreateColormap(dpy, RootWindow(dpy, vi.screen), vi.visual, AllocNone); - + u32 CWflags = CWBackingStore|CWBitGravity|CWBackPixel|CWBorderPixel|CWColormap|CWEventMask; linuxvars.win = XCreateWindow(dpy, RootWindow(dpy, vi.screen), 0, 0, w, h, 0, vi.depth, InputOutput, vi.visual, CWflags, &swa); - + if (!linuxvars.win){ system_error_box("XCreateWindow failed. Make sure your display is set up correctly."); } - + //NOTE(inso): Set the window's type to normal XChangeProperty(linuxvars.dpy, linuxvars.win, linuxvars.atom__NET_WM_WINDOW_TYPE, XA_ATOM, 32, PropModeReplace, (unsigned char*)&linuxvars.atom__NET_WM_WINDOW_TYPE_NORMAL, 1); - + //NOTE(inso): window managers want the PID as a window property for some reason. pid_t pid = getpid(); XChangeProperty(linuxvars.dpy, linuxvars.win, linuxvars.atom__NET_WM_PID, XA_CARDINAL, 32, PropModeReplace, (unsigned char*)&pid, 1); - + //NOTE(inso): set wm properties XStoreName(linuxvars.dpy, linuxvars.win, WINDOW_NAME); - + XSizeHints *sz_hints = XAllocSizeHints(); XWMHints *wm_hints = XAllocWMHints(); XClassHint *cl_hints = XAllocClassHint(); - + sz_hints->flags = PMinSize | PMaxSize | PWinGravity; - + sz_hints->min_width = 50; sz_hints->min_height = 50; - + sz_hints->max_width = sz_hints->max_height = (1UL << 16UL); sz_hints->win_gravity = NorthWestGravity; - + if (settings->set_window_pos){ sz_hints->flags |= USPosition; sz_hints->x = settings->window_x; sz_hints->y = settings->window_y; } - + wm_hints->flags |= InputHint | StateHint; wm_hints->input = True; wm_hints->initial_state = NormalState; - + cl_hints->res_name = "4coder"; cl_hints->res_class = "4coder"; - + char* win_name_list[] = { WINDOW_NAME }; XTextProperty win_name; XStringListToTextProperty(win_name_list, 1, &win_name); - + XSetWMProperties(linuxvars.dpy, linuxvars.win, &win_name, NULL, argv, argc, sz_hints, wm_hints, cl_hints); - + XFree(win_name.value); XFree(sz_hints); XFree(wm_hints); XFree(cl_hints); - + linux_set_icon(linuxvars.dpy, linuxvars.win); - + // NOTE(inso): make the window visible XMapWindow(linuxvars.dpy, linuxvars.win); - + if(!glx_create_context(fb_config)) { system_error_box("Unable to create GLX context."); } - + XRaiseWindow(linuxvars.dpy, linuxvars.win); - + if (settings->set_window_pos){ XMoveWindow(linuxvars.dpy, linuxvars.win, settings->window_x, settings->window_y); } - + if (settings->maximize_window){ linux_set_wm_state(linuxvars.atom__NET_WM_STATE_MAXIMIZED_HORZ, linuxvars.atom__NET_WM_STATE_MAXIMIZED_VERT, WM_STATE_ADD); } else if (settings->fullscreen_window){ linux_set_wm_state(linuxvars.atom__NET_WM_STATE_FULLSCREEN, 0, WM_STATE_ADD); } - + XSync(linuxvars.dpy, False); - + Atom wm_protos[] = { linuxvars.atom_WM_DELETE_WINDOW, linuxvars.atom__NET_WM_PING }; - + XSetWMProtocols(linuxvars.dpy, linuxvars.win, wm_protos, 2); - + // XFixes extension for clipboard notification. { int xfixes_version_unused, xfixes_err_unused; Bool has_xfixes = XQueryExtension(linuxvars.dpy, "XFIXES", &xfixes_version_unused, &linuxvars.xfixes_selection_event, &xfixes_err_unused); linuxvars.has_xfixes = (has_xfixes == True); - + // request notifications for CLIPBOARD updates. if(has_xfixes) { XFixesSelectSelectionInput(linuxvars.dpy, linuxvars.win, linuxvars.atom_CLIPBOARD, XFixesSetSelectionOwnerNotifyMask); } } - + // Input handling init - + setlocale(LC_ALL, ""); XSetLocaleModifiers(""); b32 locale_supported = XSupportsLocale(); - + if (!locale_supported){ setlocale(LC_ALL, "C"); } - + linuxvars.xim = XOpenIM(dpy, 0, 0, 0); if (!linuxvars.xim){ // NOTE(inso): Try falling back to the internal XIM implementation that @@ -937,16 +937,16 @@ linux_x11_init(int argc, char** argv, Plat_Settings* settings) { XSetLocaleModifiers("@im=none"); linuxvars.xim = XOpenIM(dpy, 0, 0, 0); } - + // If it still isn't there we're screwed. if (!linuxvars.xim){ system_error_box("Could not initialize X Input."); } - + XIMStyles *styles = NULL; const XIMStyle style_want = (XIMPreeditNothing | XIMStatusNothing); b32 found_style = false; - + if (!XGetIMValues(linuxvars.xim, XNQueryInputStyle, &styles, NULL) && styles){ for (i32 i = 0; i < styles->count_styles; ++i){ XIMStyle style = styles->supported_styles[i]; @@ -956,28 +956,28 @@ linux_x11_init(int argc, char** argv, Plat_Settings* settings) { } } } - + if(!found_style) { system_error_box("Could not find supported X Input style."); } - + XFree(styles); - + linuxvars.xic = XCreateIC(linuxvars.xim, XNInputStyle, style_want, XNClientWindow, linuxvars.win, XNFocusWindow, linuxvars.win, NULL); - + if(!linuxvars.xic) { system_error_box("Error creating X Input context."); } - + int xim_event_mask; if (XGetICValues(linuxvars.xic, XNFilterEvents, &xim_event_mask, NULL)){ xim_event_mask = 0; } - + u32 event_mask = ExposureMask | KeyPressMask | KeyReleaseMask | ButtonPressMask | ButtonReleaseMask @@ -987,24 +987,24 @@ linux_x11_init(int argc, char** argv, Plat_Settings* settings) { | StructureNotifyMask | ExposureMask | VisibilityChangeMask | xim_event_mask; - + XSelectInput(linuxvars.dpy, linuxvars.win, event_mask); - + // init XKB keyboard extension - + if(!XkbQueryExtension(linuxvars.dpy, 0, &linuxvars.xkb_event, 0, 0, 0)) { system_error_box("XKB Extension not available."); } - + XkbSelectEvents(linuxvars.dpy, XkbUseCoreKbd, XkbAllEventsMask, XkbAllEventsMask); linuxvars.xkb = XkbGetKeyboard(linuxvars.dpy, XkbAllComponentsMask, XkbUseCoreKbd); if(!linuxvars.xkb) { system_error_box("Error getting XKB keyboard details."); } - + // closer to windows behaviour (holding key doesn't generate release events) XkbSetDetectableAutoRepeat(linuxvars.dpy, True, NULL); - + XCursor cursors[APP_MOUSE_CURSOR_COUNT] = { None, None, @@ -1013,15 +1013,15 @@ linux_x11_init(int argc, char** argv, Plat_Settings* settings) { XCreateFontCursor(linuxvars.dpy, XC_sb_v_double_arrow) }; block_copy(linuxvars.xcursors, cursors, sizeof(cursors)); - + // sneaky invisible cursor { char data = 0; XColor c = {}; Pixmap p = XCreateBitmapFromData(linuxvars.dpy, linuxvars.win, &data, 1, 1); - + linuxvars.hidden_cursor = XCreatePixmapCursor(linuxvars.dpy, p, p, &c, &c, 0, 0); - + XFreePixmap(linuxvars.dpy, p); } } @@ -1030,9 +1030,9 @@ global Key_Code keycode_lookup_table[255]; internal void linux_keycode_init(Display* dpy){ - + block_zero_array(keycode_lookup_table); - + // Find these keys by physical position, and map to QWERTY KeyCodes #define K(k) glue(KeyCode_, k) static const u8 positional_keys[] = { @@ -1042,27 +1042,27 @@ linux_keycode_init(Display* dpy){ K(Z), K(X), K(C), K(V), K(B), K(N), K(M), K(Comma), K(Period), K(ForwardSlash), 0, 0 }; #undef K - + // XKB gives the alphanumeric keys names like AE01 -> E is the row (from B-E), 01 is the column (01-12). // to get key names in .ps file: setxkbmap -print | xkbcomp - - | xkbprint -label name - out.ps - + static const int ncols = 12; static const int nrows = 4; - + for(int i = XkbMinLegalKeyCode; i <= XkbMaxLegalKeyCode; ++i) { const char* name = linuxvars.xkb->names->keys[i].name; - + if(name[0] == 'A' && name[1] >= 'B' && name[1] <= 'E') { int row = (nrows - 1) - (name[1] - 'B'); int col = (name[2] - '0') * 10 + (name[3] - '0') - 1; - + if(row >= 0 && row < nrows && col >= 0 && col < ncols) { keycode_lookup_table[i] = positional_keys[row * ncols + col]; } } - + // a few special cases: - + else if(memcmp(name, "TLDE", XkbKeyNameLength) == 0) { keycode_lookup_table[i] = KeyCode_Tick; } else if(memcmp(name, "BKSL", XkbKeyNameLength) == 0) { @@ -1074,12 +1074,12 @@ linux_keycode_init(Display* dpy){ // keycode_lookup_table[i] = } } - + // Find the rest by their key label struct SymCode { KeySym sym; Key_Code code; }; SymCode sym_table[100]; SymCode* p = sym_table; - + *p++ = { XK_space, KeyCode_Space }; *p++ = { XK_Tab, KeyCode_Tab }; *p++ = { XK_Escape, KeyCode_Escape }; @@ -1108,22 +1108,22 @@ linux_keycode_init(Display* dpy){ *p++ = { XK_Alt_R, KeyCode_Alt }; *p++ = { XK_Super_L, KeyCode_Command }; *p++ = { XK_Super_R, KeyCode_Command }; - + for(Key_Code k = KeyCode_F1; k <= KeyCode_F16; ++k) { *p++ = { XK_F1 + (k - KeyCode_F1), k }; } - + const int table_size = p - sym_table; Assert(table_size < ArrayCount(sym_table)); - + for(int i = XkbMinLegalKeyCode; i <= XkbMaxLegalKeyCode; ++i) { KeySym sym = NoSymbol; - + // lookup key in current layout with no modifiers held (0) if(!XkbTranslateKeyCode(linuxvars.xkb, i, XkbBuildCoreState(0, linuxvars.xkb_group), NULL, &sym)) { continue; } - + for(int j = 0; j < table_size; ++j) { if(sym_table[j].sym == sym) { keycode_lookup_table[i] = sym_table[j].code; @@ -1137,20 +1137,20 @@ internal void linux_epoll_init(void) { struct epoll_event e = {}; e.events = EPOLLIN | EPOLLET; - + linuxvars.step_timer_fd = timerfd_create(CLOCK_MONOTONIC, TFD_NONBLOCK); linuxvars.epoll = epoll_create(16); - + e.data.ptr = &epoll_tag_x11; epoll_ctl(linuxvars.epoll, EPOLL_CTL_ADD, ConnectionNumber(linuxvars.dpy), &e); - + e.data.ptr = &epoll_tag_step_timer; epoll_ctl(linuxvars.epoll, EPOLL_CTL_ADD, linuxvars.step_timer_fd, &e); } internal void linux_clipboard_send(XSelectionRequestEvent* req) { - + XSelectionEvent rsp = {}; rsp.type = SelectionNotify; rsp.requestor = req->requestor; @@ -1158,87 +1158,87 @@ linux_clipboard_send(XSelectionRequestEvent* req) { rsp.target = req->target; rsp.time = req->time; rsp.property = None; - + Atom formats[] = { linuxvars.atom_UTF8_STRING, XA_STRING, }; - + if(linuxvars.clipboard_contents.size == 0) { goto done; } - + if(req->selection != linuxvars.atom_CLIPBOARD || req->property == None) { goto done; } - + if (req->target == linuxvars.atom_TARGETS){ - + XChangeProperty( - req->display, - req->requestor, - req->property, - XA_ATOM, - 32, - PropModeReplace, - (u8*)formats, - ArrayCount(formats)); - + req->display, + req->requestor, + req->property, + XA_ATOM, + 32, + PropModeReplace, + (u8*)formats, + ArrayCount(formats)); + rsp.property = req->property; - + } else { - + int i; for(i = 0; i < ArrayCount(formats); ++i){ if (req->target == formats[i]){ break; } } - + if (i != ArrayCount(formats)){ XChangeProperty( - req->display, - req->requestor, - req->property, - req->target, - 8, - PropModeReplace, - linuxvars.clipboard_contents.str, - linuxvars.clipboard_contents.size - ); - + req->display, + req->requestor, + req->property, + req->target, + 8, + PropModeReplace, + linuxvars.clipboard_contents.str, + linuxvars.clipboard_contents.size + ); + rsp.property = req->property; } } - -done: + + done: XSendEvent(req->display, req->requestor, True, 0, (XEvent*)&rsp); } internal void linux_clipboard_recv(XSelectionEvent* ev) { - + if(ev->selection != linuxvars.atom_CLIPBOARD || - ev->target != linuxvars.atom_UTF8_STRING || - ev->property == None) { + ev->target != linuxvars.atom_UTF8_STRING || + ev->property == None) { return; } - + Atom type; int fmt; unsigned long nitems; unsigned long bytes_left; u8 *data; - + int result = XGetWindowProperty( - linuxvars.dpy, - linuxvars.win, - linuxvars.atom_CLIPBOARD, - 0L, 0x20000000L, False, - linuxvars.atom_UTF8_STRING, - &type, &fmt, &nitems, - &bytes_left, &data); - + linuxvars.dpy, + linuxvars.win, + linuxvars.atom_CLIPBOARD, + 0L, 0x20000000L, False, + linuxvars.atom_UTF8_STRING, + &type, &fmt, &nitems, + &bytes_left, &data); + if(result == Success && fmt == 8){ linalloc_clear(linuxvars.clipboard_arena); linuxvars.clipboard_contents = push_u8_stringf(linuxvars.clipboard_arena, "%.*s", nitems, data); @@ -1253,17 +1253,17 @@ internal String_Const_u8 linux_filter_text(Arena* arena, u8* buf, int len) { u8* const result = push_array(arena, u8, len); u8* outp = result; - + for(int i = 0; i < len; ++i) { u8 c = buf[i]; - + if(c == '\r') { *outp++ = '\n'; } else if(c > 127 || (' ' <= c && c <= '~') || c == '\t') { *outp++ = c; } } - + return SCu8(result, outp - result); } @@ -1271,47 +1271,47 @@ internal void linux_handle_x11_events() { static XEvent prev_event = {}; b32 should_step = false; - + while (XPending(linuxvars.dpy)) { XEvent event; XNextEvent(linuxvars.dpy, &event); - + if (XFilterEvent(&event, None) == True){ continue; } - + switch(event.type) { case KeyPress: { should_step = true; - + Input_Modifier_Set_Fixed* mods = &linuxvars.input.pers.modifiers; - + int state = event.xkey.state; set_modifier(mods, KeyCode_Shift, state & ShiftMask); set_modifier(mods, KeyCode_Control, state & ControlMask); set_modifier(mods, KeyCode_CapsLock, state & LockMask); set_modifier(mods, KeyCode_Alt, state & Mod1Mask); - + event.xkey.state &= ~(ControlMask); - + Status status; KeySym keysym = NoSymbol; u8 buf[256] = {}; - + int len = Xutf8LookupString(linuxvars.xic, &event.xkey, (char*)buf, sizeof(buf) - 1, &keysym, &status); - + if (status == XBufferOverflow){ Xutf8ResetIC(linuxvars.xic); XSetICFocus(linuxvars.xic); } - + if (keysym == XK_ISO_Left_Tab){ add_modifier(mods, KeyCode_Shift); } - + Key_Code key = keycode_lookup_table[(u8)event.xkey.keycode]; //printf("key %d = %s\n", event.xkey.keycode, key_code_name[key]); - + Input_Event* key_event = NULL; if(key) { add_modifier(mods, key); @@ -1320,7 +1320,7 @@ linux_handle_x11_events() { key_event->key.code = key; key_event->key.modifiers = copy_modifier_set(linuxvars.frame_arena, mods); } - + Input_Event* text_event = NULL; if(status == XLookupChars || status == XLookupBoth) { String_Const_u8 str = linux_filter_text(linuxvars.frame_arena, buf, len); @@ -1330,26 +1330,26 @@ linux_handle_x11_events() { text_event->text.string = str; } } - + if(key_event && text_event) { key_event->key.first_dependent_text = text_event; } - + } break; - + case KeyRelease: { should_step = true; - + Input_Modifier_Set_Fixed* mods = &linuxvars.input.pers.modifiers; - + int state = event.xkey.state; set_modifier(mods, KeyCode_Shift, state & ShiftMask); set_modifier(mods, KeyCode_Control, state & ControlMask); set_modifier(mods, KeyCode_CapsLock, state & LockMask); set_modifier(mods, KeyCode_Alt, state & Mod1Mask); - + Key_Code key = keycode_lookup_table[(u8)event.xkey.keycode]; - + Input_Event* key_event = NULL; if(key) { remove_modifier(mods, key); @@ -1359,149 +1359,149 @@ linux_handle_x11_events() { key_event->key.modifiers = copy_modifier_set(linuxvars.frame_arena, mods); } } break; - + case MotionNotify: { int x = clamp(0, event.xmotion.x, render_target.width - 1); int y = clamp(0, event.xmotion.y, render_target.height - 1); linuxvars.input.pers.mouse = { x, y }; should_step = true; } break; - + case ButtonPress: { should_step = true; switch(event.xbutton.button) { case Button1: { linuxvars.input.trans.mouse_l_press = true; linuxvars.input.pers.mouse_l = true; - + // NOTE(inso): improves selection dragging (especially in notepad-like mode). // we will still get mouse events when the pointer leaves the window if it's dragging. XGrabPointer( - linuxvars.dpy, - linuxvars.win, - True, PointerMotionMask | ButtonPressMask | ButtonReleaseMask, - GrabModeAsync, GrabModeAsync, - None, None, CurrentTime); - + linuxvars.dpy, + linuxvars.win, + True, PointerMotionMask | ButtonPressMask | ButtonReleaseMask, + GrabModeAsync, GrabModeAsync, + None, None, CurrentTime); + } break; - + case Button3: { linuxvars.input.trans.mouse_r_press = true; linuxvars.input.pers.mouse_r = true; } break; - + case Button4: { linuxvars.input.trans.mouse_wheel = -100; } break; - + case Button5: { linuxvars.input.trans.mouse_wheel = +100; } break; } } break; - + case ButtonRelease: { should_step = true; switch(event.xbutton.button) { case Button1: { linuxvars.input.trans.mouse_l_release = true; linuxvars.input.pers.mouse_l = false; - + XUngrabPointer(linuxvars.dpy, CurrentTime); } break; - + case Button3: { linuxvars.input.trans.mouse_r_release = true; linuxvars.input.pers.mouse_r = false; } break; } } break; - + case FocusIn: case FocusOut: { linuxvars.input.pers.mouse_l = false; linuxvars.input.pers.mouse_r = false; block_zero_struct(&linuxvars.input.pers.modifiers); } break; - + case EnterNotify: { linuxvars.input.pers.mouse_out_of_window = 0; } break; - + case LeaveNotify: { linuxvars.input.pers.mouse_out_of_window = 1; } break; - + case ConfigureNotify: { i32 w = event.xconfigure.width; i32 h = event.xconfigure.height; - + if (w != render_target.width || h != render_target.height){ should_step = true; render_target.width = w; render_target.height = h; } } break; - + case ClientMessage: { Atom atom = event.xclient.data.l[0]; - + // Window X button clicked if(atom == linuxvars.atom_WM_DELETE_WINDOW) { should_step = true; linuxvars.input.trans.trying_to_kill = true; } - + // Notify WM that we're still responding (don't grey our window out). else if(atom == linuxvars.atom__NET_WM_PING) { event.xclient.window = DefaultRootWindow(linuxvars.dpy); XSendEvent( - linuxvars.dpy, - event.xclient.window, - False, - SubstructureRedirectMask | SubstructureNotifyMask, - &event); + linuxvars.dpy, + event.xclient.window, + False, + SubstructureRedirectMask | SubstructureNotifyMask, + &event); } } break; - + case SelectionRequest: { linux_clipboard_send((XSelectionRequestEvent*)&event); } break; - + case SelectionNotify: { linux_clipboard_recv((XSelectionEvent*)&event); } break; - + case SelectionClear: { if(event.xselectionclear.selection == linuxvars.atom_CLIPBOARD) { linalloc_clear(linuxvars.clipboard_arena); block_zero_struct(&linuxvars.clipboard_contents); } } break; - + case Expose: case VisibilityNotify: { should_step = true; } break; - + default: { // clipboard update notification - ask for the new content if (linuxvars.clipboard_catch_all && event.type == linuxvars.xfixes_selection_event) { XFixesSelectionNotifyEvent* sne = (XFixesSelectionNotifyEvent*)&event; if (sne->subtype == XFixesSelectionNotify && sne->owner != linuxvars.win){ XConvertSelection( - linuxvars.dpy, - linuxvars.atom_CLIPBOARD, - linuxvars.atom_UTF8_STRING, - linuxvars.atom_CLIPBOARD, - linuxvars.win, - CurrentTime); + linuxvars.dpy, + linuxvars.atom_CLIPBOARD, + linuxvars.atom_UTF8_STRING, + linuxvars.atom_CLIPBOARD, + linuxvars.win, + CurrentTime); } } - + else if(event.type == linuxvars.xkb_event) { XkbEvent* kb = (XkbEvent*)&event; - + // Keyboard layout changed, refresh lookup table. if(kb->any.xkb_type == XkbStateNotify && kb->state.group != linuxvars.xkb_group) { linuxvars.xkb_group = kb->state.group; @@ -1512,7 +1512,7 @@ linux_handle_x11_events() { } break; } } - + if(should_step) { linux_schedule_step(); } @@ -1521,20 +1521,20 @@ linux_handle_x11_events() { internal b32 linux_epoll_process(struct epoll_event* events, int num_events) { b32 do_step = false; - + for (int i = 0; i < num_events; ++i){ struct epoll_event* ev = events + i; Epoll_Kind* tag = (Epoll_Kind*)ev->data.ptr; - + switch (*tag){ case EPOLL_X11: { linux_handle_x11_events(); } break; - + case EPOLL_X11_INTERNAL: { //XProcessInternalConnection(linuxvars.dpy, fd); } break; - + case EPOLL_STEP_TIMER: { u64 count; int ret; @@ -1543,11 +1543,11 @@ linux_epoll_process(struct epoll_event* events, int num_events) { } while (ret != -1 || errno != EAGAIN); do_step = true; } break; - + case EPOLL_CLI_PIPE: { linux_schedule_step(); } break; - + case EPOLL_USER_TIMER: { Linux_Object* obj = CastFromMember(Linux_Object, timer.epoll_tag, tag); close(obj->timer.fd); @@ -1556,51 +1556,51 @@ linux_epoll_process(struct epoll_event* events, int num_events) { } break; } } - + return do_step; } int main(int argc, char **argv){ - + pthread_mutexattr_t attr; pthread_mutexattr_init(&attr); pthread_mutexattr_settype(&attr, PTHREAD_MUTEX_RECURSIVE); pthread_mutex_init(&linuxvars.memory_tracker_mutex, &attr); - + // NOTE(allen): context setup { Base_Allocator* alloc = get_base_allocator_system(); thread_ctx_init(&linuxvars.tctx, ThreadKind_Main, alloc, alloc); } - + API_VTable_system system_vtable = {}; system_api_fill_vtable(&system_vtable); - + API_VTable_graphics graphics_vtable = {}; graphics_api_fill_vtable(&graphics_vtable); - + API_VTable_font font_vtable = {}; font_api_fill_vtable(&font_vtable); - + // NOTE(allen): memory linuxvars.frame_arena = reserve_arena(&linuxvars.tctx); linuxvars.clipboard_arena = reserve_arena(&linuxvars.tctx); render_target.arena = make_arena_system(KB(256)); - + linuxvars.fontconfig = FcInitLoadConfigAndFonts(); - + linuxvars.cursor_show = MouseCursorShow_Always; linuxvars.prev_cursor_show = MouseCursorShow_Always; - + dll_init_sentinel(&linuxvars.free_linux_objects); dll_init_sentinel(&linuxvars.timer_objects); - + //InitializeCriticalSection(&win32vars.thread_launch_mutex); //InitializeConditionVariable(&win32vars.thread_launch_cv); - + linuxvars.clipboard_catch_all = true; - + // NOTE(allen): load core System_Library core_library = {}; App_Functions app = {}; @@ -1609,7 +1609,7 @@ main(int argc, char **argv){ Scratch_Block scratch(&linuxvars.tctx, Scratch_Share); Path_Search_List search_list = {}; search_list_add_system_path(scratch, &search_list, SystemPath_Binary); - + String_Const_u8 core_path = get_full_path(scratch, &search_list, SCu8("4ed_app.so")); if (system_load_library(scratch, core_path, &core_library)){ get_funcs = (App_Get_Functions*)system_get_proc(core_library, "app_get_functions"); @@ -1626,20 +1626,20 @@ main(int argc, char **argv){ system_error_box(msg); } } - + // NOTE(allen): send system vtable to core app.load_vtables(&system_vtable, &font_vtable, &graphics_vtable); // get_logger calls log_init which is needed. app.get_logger(); //linuxvars.log_string = app.get_logger(); - + // NOTE(allen): init & command line parameters Plat_Settings plat_settings = {}; void *base_ptr = 0; { Scratch_Block scratch(&linuxvars.tctx, Scratch_Share); String_Const_u8 curdir = system_get_path(scratch, SystemPath_CurrentDirectory); - + char **files = 0; i32 *file_count = 0; base_ptr = app.read_command_line(&linuxvars.tctx, curdir, &plat_settings, &files, &file_count, argc, argv); @@ -1656,7 +1656,7 @@ main(int argc, char **argv){ *file_count = j; }*/ } - + // NOTE(allen): load custom layer System_Library custom_library = {}; Custom_API custom = {}; @@ -1664,7 +1664,7 @@ main(int argc, char **argv){ char custom_not_found_msg[] = "Did not find a library for the custom layer."; char custom_fail_version_msg[] = "Failed to load custom code due to missing version information or a version mismatch. Try rebuilding with buildsuper."; char custom_fail_init_apis[] = "Failed to load custom code due to missing 'init_apis' symbol. Try rebuilding with buildsuper"; - + Scratch_Block scratch(&linuxvars.tctx, Scratch_Share); String_Const_u8 default_file_name = string_u8_litexpr("custom_4coder.so"); Path_Search_List search_list = {}; @@ -1695,11 +1695,12 @@ main(int argc, char **argv){ has_library = true; } } - + if (!has_library){ system_error_box(custom_not_found_msg); } custom.get_version = (_Get_Version_Type*)system_get_proc(custom_library, "get_version"); + printf("VER: %d.%d.%d\n", MAJOR, MINOR, PATCH); if (custom.get_version == 0 || custom.get_version(MAJOR, MINOR, PATCH) == 0){ system_error_box(custom_fail_version_msg); } @@ -1708,37 +1709,37 @@ main(int argc, char **argv){ system_error_box(custom_fail_init_apis); } } - + linux_x11_init(argc, argv, &plat_settings); linux_keycode_init(linuxvars.dpy); linux_epoll_init(); - + // app init { Scratch_Block scratch(&linuxvars.tctx, Scratch_Share); String_Const_u8 curdir = system_get_path(scratch, SystemPath_CurrentDirectory); app.init(&linuxvars.tctx, &render_target, base_ptr, curdir, custom); } - + linuxvars.global_frame_mutex = system_mutex_make(); system_mutex_acquire(linuxvars.global_frame_mutex); - + linux_schedule_step(); b32 first_step = true; - + for (;;) { - + if (XEventsQueued(linuxvars.dpy, QueuedAlready)){ linux_handle_x11_events(); } - + system_mutex_release(linuxvars.global_frame_mutex); - + struct epoll_event events[16]; int num_events = epoll_wait(linuxvars.epoll, events, ArrayCount(events), -1); - + system_mutex_acquire(linuxvars.global_frame_mutex); - + if (num_events == -1){ if (errno != EINTR){ perror("epoll_wait"); @@ -1746,31 +1747,31 @@ main(int argc, char **argv){ } continue; } - + if(!linux_epoll_process(events, num_events)) { continue; } - + linuxvars.last_step_time = system_now_time(); - + // NOTE(allen): Frame Clipboard Input // Request clipboard contents from X11 on first step, or every step if they don't have XFixes notification ability. if (first_step || (!linuxvars.has_xfixes && linuxvars.clipboard_catch_all)){ XConvertSelection(linuxvars.dpy, linuxvars.atom_CLIPBOARD, linuxvars.atom_UTF8_STRING, linuxvars.atom_CLIPBOARD, linuxvars.win, CurrentTime); } - + Application_Step_Input input = {}; - + if (linuxvars.received_new_clipboard){ input.clipboard = linuxvars.clipboard_contents; linuxvars.received_new_clipboard = false; } - + input.first_step = first_step; input.dt = frame_useconds/1000000.f; // variable? input.events = linuxvars.input.trans.event_list; input.trying_to_kill = linuxvars.input.trans.trying_to_kill; - + input.mouse.out_of_window = linuxvars.input.pers.mouse_out_of_window; input.mouse.p = linuxvars.input.pers.mouse; input.mouse.l = linuxvars.input.pers.mouse_l; @@ -1780,23 +1781,23 @@ main(int argc, char **argv){ input.mouse.press_r = linuxvars.input.trans.mouse_r_press; input.mouse.release_r = linuxvars.input.trans.mouse_r_release; input.mouse.wheel = linuxvars.input.trans.mouse_wheel; - + // NOTE(allen): Application Core Update Application_Step_Result result = {}; if (app.step != 0){ result = app.step(&linuxvars.tctx, &render_target, base_ptr, &input); } - + // NOTE(allen): Finish the Loop if (result.perform_kill){ break; } - + // NOTE(NAME): Switch to New Title if (result.has_new_title){ XStoreName(linuxvars.dpy, linuxvars.win, result.title_string); } - + // NOTE(allen): Switch to New Cursor if (result.mouse_cursor_type != linuxvars.cursor && !linuxvars.input.pers.mouse_l){ XCursor c = linuxvars.xcursors[result.mouse_cursor_type]; @@ -1805,17 +1806,17 @@ main(int argc, char **argv){ } linuxvars.cursor = result.mouse_cursor_type; } - + gl_render(&render_target); glXSwapBuffers(linuxvars.dpy, linuxvars.win); - + first_step = false; - + linalloc_clear(linuxvars.frame_arena); block_zero_struct(&linuxvars.input.trans); linuxvars.step_pending = false; } - + return 0; }