From dd01a8236552f98863899b181793486d3a3a074e Mon Sep 17 00:00:00 2001 From: jouae <51120603+jouae@users.noreply.github.com> Date: Sat, 21 Sep 2024 05:28:27 +0800 Subject: [PATCH] Port font editor to SDL2 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Replace X11 with SDL2 to improve the portability and cross-platform support Migrated window creation from X11's `XCreateWindow` to `SDL_CreateWindow`. Replaced X11 event handling with SDL2’s event loop (`SDL_PollEvent`) to capture input events such as keyboard and mouse interactions. Updated rendering to use `SDL_Renderer` and `SDL_Surface`, replacing X11's rendering functions. * Modified some key event logic: 1. SDLK_ESCAPE: ESC now exits the program. 2. SDL_QUIT: Clicking the "X" on the window exits the program. * Unchanged key event logic: 1. SDLK_q: Switches to the next font. * Rename delete function: 1. Rename delete() to delete_char() to avoid `clang-format` misinterpreting `delete()` as the C++ the keyword, which cauese an extra space to be added when running `clang-format`, turning `delete()` to `delete ()`. * Add operation instructions in the READMD. --- tools/font-edit/Makefile | 4 +- tools/font-edit/README.md | 13 +- tools/font-edit/twin-fedit.c | 309 ++++++++++++++++++++--------------- tools/font-edit/twin-fedit.h | 6 - 4 files changed, 188 insertions(+), 144 deletions(-) diff --git a/tools/font-edit/Makefile b/tools/font-edit/Makefile index 0165bfd..1b25526 100644 --- a/tools/font-edit/Makefile +++ b/tools/font-edit/Makefile @@ -1,7 +1,7 @@ TARGET = twin-fedit -CFLAGS = $(shell pkg-config --cflags cairo x11) -g -Wall -LIBS = $(shell pkg-config --libs cairo x11) +CFLAGS = $(shell pkg-config --cflags cairo) $(shell sdl2-config --cflags) -g -Wall +LIBS = $(shell pkg-config --libs cairo) $(shell sdl2-config --libs) OBJS = \ twin-fedit.o \ diff --git a/tools/font-edit/README.md b/tools/font-edit/README.md index 0170bf8..9669d86 100644 --- a/tools/font-edit/README.md +++ b/tools/font-edit/README.md @@ -5,7 +5,7 @@ screens. ## Build Dependency ```shell -sudo apt-get install libx11-dev libcairo2-dev +sudo apt-get install libsdl2-dev libcairo2-dev ``` ## Usage @@ -14,4 +14,13 @@ make ./twin-fedit < nchars ``` -(press 'q' to next character) +| Key binding | Function | +| --- | --- | +| `esc` | Exit program | +| `left mouse button` | Select a point as start point | +| `right mouse button` | Select a point as end point | +| `d` | Delete selected point| +| `f`| Replace a line with a spline | +| `q` | Next character | +| `s` | Split a spline into two splines by start point and end point | +| `u` | Undo the last operation | diff --git a/tools/font-edit/twin-fedit.c b/tools/font-edit/twin-fedit.c index 12e2610..3f6b26c 100644 --- a/tools/font-edit/twin-fedit.c +++ b/tools/font-edit/twin-fedit.c @@ -22,57 +22,50 @@ #include "twin-fedit.h" -static Display *dpy; -static Window win; -static Visual *visual; -static int depth; +#include +#include +#include +#include + +static SDL_Window *window; +static cairo_t *cr; +static cairo_surface_t *surface; + static int width = 512; static int height = 512; static double scale = 8; -static cairo_t *cr; -static cairo_surface_t *surface; static int offset; static int offsets[1024]; +/* Control the life of the widown, + * if the value is false, the windown lives, + * otherwise the windown quit. + */ +static bool exit_window = false; + static int init(int argc, char **argv) { - int scr; - XSetWindowAttributes wa; - XTextProperty wm_name, icon_name; - XSizeHints sizeHints; - XWMHints wmHints; - Atom wm_delete_window; - - dpy = XOpenDisplay(0); - scr = DefaultScreen(dpy); - visual = DefaultVisual(dpy, scr); - depth = DefaultDepth(dpy, scr); - - wa.background_pixel = WhitePixel(dpy, scr); - wa.event_mask = - (KeyPressMask | KeyReleaseMask | ButtonPressMask | ButtonReleaseMask | - PointerMotionMask | ExposureMask | StructureNotifyMask); - - wm_name.value = (unsigned char *) argv[0]; - wm_name.encoding = XA_STRING; - wm_name.format = 8; - wm_name.nitems = strlen((char *) wm_name.value) + 1; - icon_name = wm_name; - - win = - XCreateWindow(dpy, RootWindow(dpy, scr), 0, 0, width, height, 0, depth, - InputOutput, visual, CWBackPixel | CWEventMask, &wa); - sizeHints.flags = 0; - wmHints.flags = InputHint; - wmHints.input = True; - XSetWMProperties(dpy, win, &wm_name, &icon_name, argv, argc, &sizeHints, - &wmHints, 0); - XSetWMProtocols(dpy, win, &wm_delete_window, 1); - - XMapWindow(dpy, win); - - surface = cairo_xlib_surface_create(dpy, win, visual, width, height); + if (SDL_Init(SDL_INIT_VIDEO) < 0) { + printf("Failed to initialize SDL video. Reason: %s\n", SDL_GetError()); + return 0; + } + + window = SDL_CreateWindow("Font Editor", SDL_WINDOWPOS_CENTERED, + SDL_WINDOWPOS_CENTERED, width, height, + SDL_WINDOW_SHOWN); + if (!window) { + printf("Failed to create SDL window. Reason: %s\n", SDL_GetError()); + return 0; + } + + /* Create an SDL surface linked to the window */ + SDL_Surface *sdl_surface = SDL_GetWindowSurface(window); + + /* Create Cairo surface based on the SDL surface */ + surface = cairo_image_surface_create_for_data( + (unsigned char *) sdl_surface->pixels, CAIRO_FORMAT_ARGB32, + sdl_surface->w, sdl_surface->h, sdl_surface->pitch); cr = cairo_create(surface); @@ -246,7 +239,14 @@ static void draw_char(char_t *c) cmd_stack_t *s; int i; - XClearArea(dpy, win, 0, 0, 0, 0, False); + /* Clear the SDL surface */ + SDL_Surface *sdl_surface = SDL_GetWindowSurface(window); + /* Fill with white color to clear */ + SDL_FillRect(sdl_surface, NULL, + SDL_MapRGB(sdl_surface->format, 255, 255, 255)); + + /* Set up Cairo to draw on the surface */ + cairo_save(cr); for (cmd = c->cmd; cmd; cmd = cmd->next) { double alpha; @@ -328,21 +328,32 @@ static void draw_char(char_t *c) tx = cmd->pt[0].x; ty = cmd->pt[0].y; } - { - cairo_save(cr); - if (cmd == c->first) - cairo_set_source_rgb(cr, 0, .5, 0); - else if (cmd == c->last) - cairo_set_source_rgb(cr, 0, 0, .5); - else - cairo_set_source_rgb(cr, 0, .5, .5); - - cairo_move_to(cr, tx - 2, ty + 3); - snprintf(buf, sizeof(buf), "%d", i); - cairo_show_text(cr, buf); - cairo_restore(cr); + + /* Save state before rendering text */ + cairo_save(cr); + if (cmd == c->first) { + /* Green for the first command */ + cairo_set_source_rgb(cr, 0, .5, 0); + } else if (cmd == c->last) { + /* Blue for the last command */ + cairo_set_source_rgb(cr, 0, 0, .5); + } else { + /* Cyan for intermediate commands */ + cairo_set_source_rgb(cr, 0, .5, .5); } + + cairo_move_to(cr, tx - 2, ty + 3); + /* Label with the index and draw */ + snprintf(buf, sizeof(buf), "%d", i); + cairo_show_text(cr, buf); + /* Restore after text drawing */ + cairo_restore(cr); } + + cairo_restore(cr); + + /* Finally, update the SDL surface with the new Cairo drawing */ + SDL_UpdateWindowSurface(window); } static cmd_t *pos_to_cmd(char_t *c, cmd_t *start, int ix, int iy) @@ -448,7 +459,7 @@ static void split(char_t *c, cmd_t *first, cmd_t *last) c->first = c->last = 0; } -static void delete(char_t *c, cmd_t *first) +static void delete_char(char_t *c, cmd_t *first) { push(c); delete_cmd(&c->cmd, first); @@ -469,21 +480,20 @@ static void undo(char_t *c) pop(c); } -static void button(char_t *c, XButtonEvent *bev) +static void button(char_t *c, SDL_MouseButtonEvent *bev) { - cmd_t *first = bev->button == 1 ? c->first : c->last; + cmd_t *first = bev->button == SDL_BUTTON_LEFT ? c->first : c->last; cmd_t *where = pos_to_cmd(c, first, bev->x, bev->y); if (!where) { - XBell(dpy, 50); + SDL_Log("Button click outside target"); return; } switch (bev->button) { - case 1: + case SDL_BUTTON_LEFT: c->first = where; break; - case 2: - case 3: + case SDL_BUTTON_RIGHT: c->last = where; break; } @@ -492,95 +502,120 @@ static void button(char_t *c, XButtonEvent *bev) static void play(char_t *c) { - XEvent ev; - char key_string[10]; + /* Ensures that SDL only focuses on key events*/ + SDL_StopTextInput(); + + SDL_Event event; + SDL_Keycode key_event; + + /* keep track of the selected spline */ + cmd_t *spline = NULL; + draw_char(c); - XClearArea(dpy, win, 0, 0, 0, 0, True); for (;;) { - XNextEvent(dpy, &ev); - switch (ev.type) { - case KeyPress: - if (XLookupString((XKeyEvent *) &ev, key_string, sizeof(key_string), - 0, 0) == 1) { - switch (key_string[0]) { - case 'q': + while (SDL_PollEvent(&event)) { + switch (event.type) { + /* If SDL event is detected */ + case SDL_QUIT: + /* Click the "X" on the top of screen to exit the program */ + exit_window = true; + return; + case SDL_KEYDOWN: + /* If any key event is detected */ + key_event = event.key.keysym.sym; + + switch (key_event) { + case SDLK_q: + /* To exit play() + * This allows the user to display the next character. + */ return; - case 'c': - XClearArea(dpy, ev.xkey.window, 0, 0, 0, 0, True); - break; - case 's': + case SDLK_ESCAPE: + /* To quit the program */ + exit_window = true; + return; + case SDLK_s: + /* To split the command between first char and last char */ if (c->first && c->last) { split(c, c->first, c->last); - draw_char(c); } + draw_char(c); break; - case 'u': + case SDLK_u: + /* To undo the last operation */ undo(c); draw_char(c); break; - case 'f': + case SDLK_f: + /* To replace with spline between first and last */ if (c->first && c->last) { replace_with_spline(c, c->first, c->last); - draw_char(c); } + draw_char(c); break; - case 'd': + case SDLK_d: + /* To delete the first command */ if (c->first) { - delete (c, c->first); - draw_char(c); + delete_char(c, c->first); } + draw_char(c); break; - } - } else { - cmd_t *spline; - if (c->first && c->first->op == op_curve) - spline = c->first; - else if (c->last && c->last->op == op_curve) - spline = c->last; - else - spline = 0; - if (spline) { - int keysyms_keycode; - KeySym *keysym = XGetKeyboardMapping(dpy, ev.xkey.keycode, - 1, &keysyms_keycode); - switch (keysyms_keycode) { - case XK_Left: - tweak_spline(c, spline, ev.xkey.state & ShiftMask, -1, - 0); - draw_char(c); + /* Move the point of c->first */ + case SDLK_LEFT: + if (!spline) break; - case XK_Right: - tweak_spline(c, spline, ev.xkey.state & ShiftMask, 1, - 0); - draw_char(c); + tweak_spline(c, spline, event.key.keysym.mod & KMOD_SHIFT, + -1, 0); + draw_char(c); + break; + case SDLK_RIGHT: + if (!spline) break; - case XK_Up: - tweak_spline(c, spline, ev.xkey.state & ShiftMask, 0, - -1); - draw_char(c); + + tweak_spline(c, spline, event.key.keysym.mod & KMOD_SHIFT, + 1, 0); + draw_char(c); + break; + case SDLK_UP: + if (!spline) break; - case XK_Down: - tweak_spline(c, spline, ev.xkey.state & ShiftMask, 0, - 1); - draw_char(c); + + tweak_spline(c, spline, event.key.keysym.mod & KMOD_SHIFT, + 0, -1); + draw_char(c); + break; + case SDLK_DOWN: + if (!spline) break; - } - XFree(keysym); + + tweak_spline(c, spline, event.key.keysym.mod & KMOD_SHIFT, + 0, 1); + draw_char(c); + break; } - } - break; - case Expose: - if (ev.xexpose.count == 0) + /* Ensure the content is redrawn after every key press */ + break; + /* End if key event detected */ + case SDL_WINDOWEVENT: + if (event.window.event == SDL_WINDOWEVENT_EXPOSED) + break; + break; + case SDL_MOUSEBUTTONDOWN: + /* Redraw the content after mouse button interaction */ + button(c, &event.button); + spline = c->first; draw_char(c); - break; - case ButtonPress: - button(c, &ev.xbutton); - break; + break; + } + /* End if SDL event detected */ } + + /* Ensure the SDL window surface is updated */ + SDL_UpdateWindowSurface(window); } } -static void write_char(char_t *c) +static void print_char(char_t *c) { cmd_t *cmd; @@ -608,23 +643,29 @@ static void write_char(char_t *c) offset += 1; } -int main(int argc, char **argv) +static void generate_font_metrics() { - char_t *c; int ucs4; - - if (!init(argc, argv)) - exit(1); - while ((c = read_char())) { - play(c); - write_char(c); - } for (ucs4 = 0; ucs4 < 0x80; ucs4++) { if ((ucs4 & 7) == 0) printf("\n "); printf(" %4d,", offsets[ucs4]); } printf("\n"); +} + +int main(int argc, char **argv) +{ + char_t *c; + + if (!init(argc, argv)) + exit(1); + while ((c = read_char()) && !exit_window) { + play(c); + print_char(c); + } + + generate_font_metrics(); return 0; } diff --git a/tools/font-edit/twin-fedit.h b/tools/font-edit/twin-fedit.h index ef8e2f3..475bd37 100644 --- a/tools/font-edit/twin-fedit.h +++ b/tools/font-edit/twin-fedit.h @@ -23,12 +23,6 @@ #ifndef _TWIN_FEDIT_H_ #define _TWIN_FEDIT_H_ -#include -#include -#include -#include -#include -#include #include #include #include