diff --git a/Makefile b/Makefile index 9675166..22da89d 100644 --- a/Makefile +++ b/Makefile @@ -16,13 +16,16 @@ PREFIX = /usr/local # cc | clang | gcc CC = cc +IMPLEMENTS = -DSUWAUI_IMPLEMENTS_SUWAWINDOW \ + -DSUWAUI_IMPLEMENTS_SUWALABEL \ + -DSUWAUI_IMPLEMENTS_SUWABUTTON # lldb | gdb DEBUGGER = lldb -FILES = main.c src/*.c +FILES = *.c CFLAGS = -Wall -Wextra \ - -I./dep/include -I/usr/include -I/usr/local/include -I/usr/X11R6/include \ - -L./dep/lib/${OS} -L/usr/lib -L/usr/local/lib -L/usr/X11R6/lib \ + -I./include -I/usr/include -I/usr/local/include -I/usr/X11R6/include \ + -L/usr/lib -L/usr/local/lib -L/usr/X11R6/lib \ -I/usr/X11R6/include/freetype2 -I/usr/local/include/freetype2 LDFLAGS = -lc -lX11 -lXft @@ -39,15 +42,15 @@ SLIB += -lxcb -lpthread -lfontconfig -lz -lexpat -lfreetype -lXrender -lXau -lXd all: debug debug: - ${CC} -O0 -g ${CFLAGS} -o ${NAME} ${FILES} ${LDFLAGS} + ${CC} ${IMPLEMENTS} -O0 -g ${CFLAGS} -o ${NAME} ${FILES} ${LDFLAGS} ${DEBUGGER} -o run ${NAME} develop: - ${CC} -O3 -g ${CFLAGS} -o ${NAME} ${FILES} ${LDFLAGS} ${SLIB} + ${CC} ${IMPLEMENTS} -O3 -g ${CFLAGS} -o ${NAME} ${FILES} ${LDFLAGS} ${SLIB} ./${NAME} release: - ${CC} -O3 ${CFLAGS} -o ${NAME} ${FILES} -static ${LDFLAGS} ${SLIB} + ${CC} ${IMPLEMENTS} -O3 ${CFLAGS} -o ${NAME} ${FILES} -static ${LDFLAGS} ${SLIB} strip ${NAME} ./${NAME} diff --git a/src/control.c b/control.c similarity index 51% rename from src/control.c rename to control.c index 20aec58..a9d3ca1 100644 --- a/src/control.c +++ b/control.c @@ -3,7 +3,6 @@ #include #include "control.h" -#include "display.h" #define NUM_ROWS 6 #define NUM_COLS 4 @@ -18,8 +17,6 @@ char curinput[64] = {0}; int input_pos = 0; -int initialized = 0; -XftColor buttonColor; static const char *btn_labels[][10] = { { NULL }, // 数字表示 @@ -30,18 +27,10 @@ static const char *btn_labels[][10] = { { "0", ".", "=", "<" }, }; -static inline float get_scale(UiSystem *ui) { - XWindowAttributes attr; - XGetWindowAttributes(ui->display, ui->xwindow, &attr); - float sx = (float)attr.width / DESIGN_WIDTH; - float sy = (float)attr.height / DESIGN_HEIGHT; - return sx < sy ? sx : sy; -} - -static SuwaButton *find_button_at(UiSystem *ui, int mx, int my) { +static SuwaButton *find_button_at(SuwaWindow *window, int mx, int my) { static SuwaButton found = {0}; - float scale = get_scale(ui); + float scale = get_scale(window); if (scale <= 0) scale = 1.0f; int btn_w = (int)(DESIGN_BUTTON_WIDTH * scale + .5f); @@ -82,29 +71,29 @@ static SuwaButton *find_button_at(UiSystem *ui, int mx, int my) { return &found; } -void append_to_input(UiSystem *ui, char c) { +void append_to_input(CtrlLabels *labels, char c) { if ((unsigned long)input_pos >= sizeof(curinput) - 2) return; - strcpy(ui->problemLabel.text, ""); + strcpy(labels->problem->text, ""); curinput[input_pos++] = c; curinput[input_pos] = '\0'; - strncpy(ui->resLabel.text, curinput, sizeof(ui->resLabel.text) - 1); - ui->resLabel.text[sizeof(ui->resLabel.text) - 1] = '\0'; + strncpy(labels->res->text, curinput, sizeof(labels->res->text) - 1); + labels->res->text[sizeof(labels->res->text) - 1] = '\0'; } -void clear_calculator(UiSystem *ui) { - strcpy(ui->problemLabel.text, ""); +void clear_calculator(CtrlLabels *labels) { + strcpy(labels->problem->text, ""); curinput[0] = '\0'; input_pos = 0; - strcpy(ui->resLabel.text, "0"); + strcpy(labels->res->text, "0"); } -double evaluate_simple(UiSystem *ui, const char *expr) { +double evaluate_simple(CtrlLabels *labels, const char *expr) { double res = 0.0; double cur = 0.0; char op = '+'; int i = 0; - snprintf(ui->problemLabel.text, sizeof(ui->problemLabel.text), "%s=", expr); + snprintf(labels->problem->text, sizeof(labels->problem->text), "%s=", expr); while (expr[i]) { if (expr[i] == ' ') { i++; continue; } @@ -144,35 +133,35 @@ double evaluate_simple(UiSystem *ui, const char *expr) { return res; } -void control_expose(UiSystem *ui, XEvent *e) { - if (e->type != Expose && e->type != ConfigureNotify) return; +void control_expose(SuwaWindow *window, CtrlLabels *labels) { + if (window->event.type != Expose && window->event.type != ConfigureNotify) return; XWindowAttributes attr; - XGetWindowAttributes(ui->display, ui->xwindow, &attr); + XGetWindowAttributes(window->display, window->xwindow, &attr); int w = attr.width; int h = attr.height; - if (ui->backbuf != None) XFreePixmap(ui->display, ui->backbuf); - ui->backbuf = XCreatePixmap(ui->display, ui->xwindow, w, h, - DefaultDepth(ui->display, DefaultScreen(ui->display))); + if (window->backbuf != None) XFreePixmap(window->display, window->backbuf); + window->backbuf = XCreatePixmap(window->display, window->xwindow, w, h, + DefaultDepth(window->display, DefaultScreen(window->display))); - if (ui->backbuf == None) ui->backbuf = ui->xwindow; - ui->target = ui->backbuf; + if (window->backbuf == None) window->backbuf = window->xwindow; + window->target = window->backbuf; - XftDraw *backdraw = XftDrawCreate(ui->display, ui->backbuf, - DefaultVisual(ui->display, DefaultScreen(ui->display)), - DefaultColormap(ui->display, DefaultScreen(ui->display))); + XftDraw *backdraw = XftDrawCreate(window->display, window->backbuf, + DefaultVisual(window->display, DefaultScreen(window->display)), + DefaultColormap(window->display, DefaultScreen(window->display))); if (!backdraw) { fprintf(stderr, "Pixmap向けXftDrawの作成に失敗。\n"); - XFreePixmap(ui->display, ui->backbuf); - ui->backbuf = None; + XFreePixmap(window->display, window->backbuf); + window->backbuf = None; return; } - XSetForeground(ui->display, ui->gc, BGCOL); - XFillRectangle(ui->display, ui->backbuf, ui->gc, 0, 0, w, h); + XSetForeground(window->display, window->gc, BGCOL); + XFillRectangle(window->display, window->backbuf, window->gc, 0, 0, w, h); - float scale = get_scale(ui); + float scale = get_scale(window); if (scale <= 0) scale = 1.f; int label_padding = (int)(20 * scale + .5f); @@ -180,143 +169,132 @@ void control_expose(UiSystem *ui, XEvent *e) { // 出力 { XGlyphInfo extents; - XftTextExtentsUtf8(ui->display, ui->resLabel.font, - (const FcChar8 *)ui->resLabel.text, - strlen(ui->resLabel.text), &extents); + XftTextExtentsUtf8(window->display, labels->res->font, + (const FcChar8 *)labels->res->text, + strlen(labels->res->text), &extents); int tx = w - label_padding - extents.xOff; int ty = (int)(180 * scale + .5f); XftDrawStringUtf8( - backdraw, &ui->resLabel.fg_color, ui->resLabel.font, - tx, ty, (const FcChar8 *)ui->resLabel.text, 64); + backdraw, &labels->res->fg_color, labels->res->font, + tx, ty, (const FcChar8 *)labels->res->text, 64); } { XGlyphInfo extents; - XftTextExtentsUtf8(ui->display, ui->problemLabel.font, - (const FcChar8 *)ui->problemLabel.text, - strlen(ui->problemLabel.text), &extents); + XftTextExtentsUtf8(window->display, labels->problem->font, + (const FcChar8 *)labels->problem->text, + strlen(labels->problem->text), &extents); int tx = w - label_padding - extents.xOff; int ty = (int)(80 * scale + .5f); XftDrawStringUtf8( - backdraw, &ui->problemLabel.fg_color, ui->problemLabel.font, - tx, ty, (const FcChar8 *)ui->problemLabel.text, 64); + backdraw, &labels->problem->fg_color, labels->problem->font, + tx, ty, (const FcChar8 *)labels->problem->text, 64); } - int width = (int)(DESIGN_BUTTON_WIDTH * scale + .5f); - int height = (int)(DESIGN_BUTTON_HEIGHT * scale + .5f); - int padding = (int)(DESIGN_PADDING * scale + .5f); - int start_y = (int)(DESIGN_BUTTON_AREA_Y * scale + .5f); + int bw = (int)(DESIGN_BUTTON_WIDTH * scale + .5f); + int bh = (int)(DESIGN_BUTTON_HEIGHT * scale + .5f); + int bp = (int)(DESIGN_PADDING * scale + .5f); + int by = (int)(DESIGN_BUTTON_AREA_Y * scale + .5f); - if (initialized == 0) { - XftColorAllocName(ui->display, - DefaultVisual(ui->display, DefaultScreen(ui->display)), - DefaultColormap(ui->display, DefaultScreen(ui->display)), + XftColor buttonColor; #if defined(__OpenBSD__) - "#232320", &buttonColor); + buttonColor = suwaui_set_button_fgcolor(window, "#232320"); #elif defined(__FreeBSD__) - "#232020", &buttonColor); + buttonColor = suwaui_set_button_fgcolor(window, "#232020"); #endif - } - initialized = 1; for (int row = 1; row < NUM_ROWS; ++row) { for (int col = 0; col < NUM_COLS; ++col) { - SuwaButton btn = { - .x = padding + col * (width + padding), - .y = start_y + (row - 1) * (height + padding), - .width = width, - .height = height, - .text = btn_labels[row][col], - .font = ui->font, - .bg_color = BTCOL, - .fg_color = buttonColor, - .pressed = 0 - }; + SuwaButton btn = suwaui_add_button(bp + col * (bw + bp), + by + (row - 1) * (bh + bp), + bw, bh, btn_labels[row][col], window->font, BTCOL, buttonColor); - drawbuttons(ui, &btn, backdraw); + suwaui_draw_button(window, &btn, backdraw); } } - XCopyArea(ui->display, ui->backbuf, ui->xwindow, ui->gc, 0, 0, w, h, 0, 0); + XCopyArea(window->display, window->backbuf, window->xwindow, window->gc, + 0, 0, w, h, 0, 0); XftDrawDestroy(backdraw); - XFlush(ui->display); + XFlush(window->display); } -void handle_button_press(UiSystem *ui, int mx, int my) { - SuwaButton *btn = find_button_at(ui, mx, my); +void handle_button_press(SuwaWindow *window, int mx, int my) { + SuwaButton *btn = find_button_at(window, mx, my); if (!btn) return; btn->pressed = 1; - XftDraw *backdraw = XftDrawCreate(ui->display, ui->backbuf, - DefaultVisual(ui->display, DefaultScreen(ui->display)), - DefaultColormap(ui->display, DefaultScreen(ui->display))); + XftDraw *backdraw = XftDrawCreate(window->display, window->backbuf, + DefaultVisual(window->display, DefaultScreen(window->display)), + DefaultColormap(window->display, DefaultScreen(window->display))); if (!backdraw) { fprintf(stderr, "Pixmap向けXftDrawの作成に失敗。\n"); - XFreePixmap(ui->display, ui->backbuf); - ui->backbuf = None; + XFreePixmap(window->display, window->backbuf); + window->backbuf = None; return; } - drawbuttons(ui, btn, backdraw); - XFlush(ui->display); + suwaui_draw_button(window, btn, backdraw); + XFlush(window->display); } -void handle_button_release(UiSystem *ui, int mx, int my) { - SuwaButton *btn = find_button_at(ui, mx, my); +void handle_button_release(SuwaWindow *window, CtrlLabels *labels, int mx, int my) { + SuwaButton *btn = find_button_at(window, mx, my); if (!btn) return; btn->pressed = 0; - XftDraw *backdraw = XftDrawCreate(ui->display, ui->backbuf, - DefaultVisual(ui->display, DefaultScreen(ui->display)), - DefaultColormap(ui->display, DefaultScreen(ui->display))); + XftDraw *backdraw = XftDrawCreate(window->display, window->backbuf, + DefaultVisual(window->display, DefaultScreen(window->display)), + DefaultColormap(window->display, DefaultScreen(window->display))); if (!backdraw) { fprintf(stderr, "Pixmap向けXftDrawの作成に失敗。\n"); - XFreePixmap(ui->display, ui->backbuf); - ui->backbuf = None; + XFreePixmap(window->display, window->backbuf); + window->backbuf = None; return; } - ui->target = ui->backbuf; - drawbuttons(ui, btn, backdraw); + window->target = window->backbuf; + suwaui_draw_button(window, btn, backdraw); XftDrawDestroy(backdraw); - XFlush(ui->display); + XFlush(window->display); const char *label = btn->text; if (strcmp(label, "C") == 0) { - clear_calculator(ui); + clear_calculator(labels); } else if (strcmp(label, "×") == 0) { - append_to_input(ui, '*'); + append_to_input(labels, '*'); } else if (strcmp(label, "÷") == 0) { - append_to_input(ui, '/'); + append_to_input(labels, '/'); } else if (strcmp(label, "<") == 0) { curinput[--input_pos] = '\0'; - strcpy(ui->resLabel.text, curinput[0] ? curinput : "0"); + strcpy(labels->res->text, curinput[0] ? curinput : "0"); } else if (strcmp(label, "=") == 0) { - double res = evaluate_simple(ui, curinput); - if (isnan(res)) strncpy(ui->resLabel.text, "Error", 5); - else snprintf(ui->resLabel.text, sizeof(ui->resLabel.text), "%.8g", res); + double res = evaluate_simple(labels, curinput); + if (isnan(res)) strncpy(labels->res->text, "Error", 5); + else snprintf(labels->res->text, sizeof(labels->res->text), "%.8g", res); } else if (strlen(label) == 1) { - append_to_input(ui, label[0]); + append_to_input(labels, label[0]); } - control_expose(ui, &(XEvent){.type = Expose}); + window->event = (XEvent){.type = Expose}; + control_expose(window, labels); } -void handle_key_press(UiSystem *ui, XEvent *event) { +void handle_key_press(SuwaWindow *window, CtrlLabels *labels) { KeySym keysym; char buf[32]; int len; (void)len; - keysym = XLookupKeysym(&event->xkey, 0); - len = XLookupString(&event->xkey, buf, sizeof(buf), &keysym, NULL); + keysym = XLookupKeysym(&window->event.xkey, 0); + len = XLookupString(&window->event.xkey, buf, sizeof(buf), &keysym, NULL); if (keysym == XK_Q) { - ui->isrunning = 0; + window->isrunning = 0; return; } @@ -324,10 +302,6 @@ void handle_key_press(UiSystem *ui, XEvent *event) { puts("標準電卓画面"); } - if (keysym == XK_S) { - puts("関数電卓画面"); - } - if (keysym == XK_P) { puts("プログラマー電卓画面"); } @@ -345,19 +319,18 @@ void handle_key_press(UiSystem *ui, XEvent *event) { puts("h = ヘルプ画面 Help screen"); puts("H = 履歴画面 History screen"); puts("B = 標準電卓画面 Basic calculator screen"); - puts("S = 関数電卓画面 Scientific calculator screen"); puts("P = プログラマー電卓画面 Programmer calculator screen"); } if (keysym == XK_C) { - clear_calculator(ui); + clear_calculator(labels); goto redraw; } if (keysym == XK_Return || keysym == XK_KP_Enter) { - double res = evaluate_simple(ui, curinput); - if (isnan(res)) strncpy(ui->resLabel.text, "Error", 5); - else snprintf(ui->resLabel.text, sizeof(ui->resLabel.text), "%.8g", res); + double res = evaluate_simple(labels, curinput); + if (isnan(res)) strncpy(labels->res->text, "Error", 5); + else snprintf(labels->res->text, sizeof(labels->res->text), "%.8g", res); goto redraw; } @@ -365,7 +338,7 @@ void handle_key_press(UiSystem *ui, XEvent *event) { || (keysym == XK_Delete && input_pos > 0) || (keysym == XK_X && input_pos > 0)) { curinput[--input_pos] = '\0'; - strcpy(ui->resLabel.text, curinput[0] ? curinput : "0"); + strcpy(labels->res->text, curinput[0] ? curinput : "0"); goto redraw; } @@ -379,28 +352,31 @@ void handle_key_press(UiSystem *ui, XEvent *event) { else if (keysym == XK_KP_Decimal || keysym == XK_period) c = '.'; if (c) { - append_to_input(ui, c); + append_to_input(labels, c); goto redraw; } return; redraw: - control_expose(ui, &(XEvent){.type = Expose}); + window->event = (XEvent){.type = Expose}; + control_expose(window, labels); } -// void handle_mouse_hover(UiSystem *ui, XEvent *event) { -// SuwaButton *hover = find_button_at(event->xmotion.x, event->xmotion.y); +// void handle_mouse_hover(SuwaWindow *window) { +// int x = window->event,xmotion.x; +// int y = window->event,xmotion.y; +// SuwaButton *hover = find_button_at(x, y); // if (hover != hovered_btn) { // if (hovered_btn) { // hovered_btn->pressed = 0; -// redraw_single_button(&ui, hovered_btn); +// redraw_single_button(&window, hovered_btn); // } // hovered_btn = hover; // if (hovered_btn) { -// redraw_single_button(&ui, hovered_btn); +// redraw_single_button(&window, hovered_btn); // } // } // } diff --git a/control.h b/control.h new file mode 100644 index 0000000..4d37b1b --- /dev/null +++ b/control.h @@ -0,0 +1,14 @@ +#pragma once + +#include + +typedef struct { + SuwaLabel *res; + SuwaLabel *problem; +} CtrlLabels; + +void control_expose(SuwaWindow *window, CtrlLabels *labels); +void handle_button_press(SuwaWindow *window, int mx, int my); +void handle_button_release(SuwaWindow *window, CtrlLabels *labels, int mx, int my); +void handle_key_press(SuwaWindow *window, CtrlLabels *labels); +// void handle_mouse_hover(SuwaWindow *window); diff --git a/include/suwaui.h b/include/suwaui.h new file mode 100644 index 0000000..53785d2 --- /dev/null +++ b/include/suwaui.h @@ -0,0 +1,344 @@ +#pragma once + +#include +#include +#include + +#include + +#define FGCOL 0xfcfcfc +#if defined(__OpenBSD__) +#define BGCOL 0x232320 +#define BTSEL 0xb8b515 +#define BTCOL 0xf1ed25 +#define BTHVR 0xecea71 +#elif defined(__FreeBSD__) +#define BGCOL 0x232020 +#define BTSEL 0xb61729 +#define BTCOL 0xee4030 +#define BTHVR 0xf35869 +#endif + +#if defined(__cplusplus) +extern "C" { +#endif + +#if defined(SUWAUI_IMPLEMENTS_SUWAWINDOW) +#include + +typedef struct { + int x, y, width, height; + int start_width, start_height; + int isrunning; + const char *name; + const char *version; + const char *res_name; + const char *class_name; + Display *display; + Window xwindow; + int screen; + Drawable target; + GC gc; + Visual visual; + XftColor color; + XftFont *font; + Colormap colormap; + Pixmap backbuf; + XEvent event; +} SuwaWindow; + +__attribute__((unused)) static void suwaui_destroy_window(SuwaWindow *w); + +__attribute__((unused)) static inline float get_scale(SuwaWindow *w) { + XWindowAttributes attr; + XGetWindowAttributes(w->display, w->xwindow, &attr); + float sx = (float)attr.width / w->start_width; + float sy = (float)attr.height / w->start_height; + return sx < sy ? sx : sy; +} + +__attribute__((unused)) static SuwaWindow suwaui_create_window(int x, int y, int width, int height, + const char *software_name, const char *software_version, + const char *res_name, const char *class_name, + const char *font, const char *fg_color, + int is_centered, int cannot_shrink, int cannot_grow) { + (void)cannot_grow; + + SuwaWindow w = {0}; + w.width = width; + w.height = height; + w.start_width = width; + w.start_height = height; + w.name = software_name; + w.version = software_version; + w.res_name = res_name; + w.class_name = class_name; + w.isrunning = 1; + + XGCValues values; + + w.display = XOpenDisplay(NULL); + if (w.display == NULL) { + fprintf(stderr, "screen open fail\n"); + return (SuwaWindow){0}; + } + + w.screen = DefaultScreen(w.display); + + if (is_centered > 0) { + int sw = DisplayWidth(w.display, w.screen); + int sh = DisplayHeight(w.display, w.screen); + w.x = (sw - w.width) / 2; + w.y = (sh - w.height) / 2; + } else { + w.x = x; + w.y = y; + } + + w.xwindow = XCreateSimpleWindow(w.display, + RootWindow(w.display, w.screen), + w.x, w.y, + w.width, w.height, + 1, BTCOL, BGCOL); + if (!w.xwindow) { + suwaui_destroy_window(&w); + fprintf(stderr, "window create fail\n"); + return (SuwaWindow){0}; + } + + if (cannot_shrink) { + XSizeHints *sizeHint = XAllocSizeHints(); + if (sizeHint) { + sizeHint->flags = PMinSize; + sizeHint->min_width = width; + sizeHint->min_height = height; + XSetWMNormalHints(w.display, w.xwindow, sizeHint); + XFree(sizeHint); + } + } + + w.backbuf = XCreatePixmap(w.display, w.xwindow, + w.width, w.height, + DefaultDepth(w.display, w.screen)); + w.target = w.backbuf; + + Atom net_wm_window_type = XInternAtom(w.display, "_NET_WM_WINDOW_TYPE", False); + Atom dialog = XInternAtom(w.display, "_NET_WM_WINDOW_TYPE_DIALOG", False); + + XChangeProperty(w.display, w.xwindow, net_wm_window_type, XA_ATOM, 32, + PropModeReplace, (unsigned char *)&dialog, 1); + + XStoreName(w.display, w.xwindow, w.name); + Atom net_wm_name = XInternAtom(w.display, "_NET_WM_NAME", False); + char displayname[16]; + snprintf(displayname, 16, "%s %s", w.name, w.version); + XChangeProperty(w.display, w.xwindow, net_wm_name, + XInternAtom(w.display, "UTF8_STRING", False), 8, + PropModeReplace, (unsigned char *)displayname, strlen(displayname)); + + XClassHint *classHint = XAllocClassHint(); + if (classHint) { + classHint->res_name = strdup(w.res_name); + classHint->res_class = strdup(w.class_name); + XSetClassHint(w.display, w.xwindow, classHint); + XFree(classHint); + } + + XSetWindowBackground(w.display, w.xwindow, BGCOL); + + XSelectInput(w.display, w.xwindow, + ExposureMask + | ButtonPressMask + | ButtonReleaseMask + | KeyPressMask + // | PointerMotionMask + // | ButtonMotionMask + // | StructureNotifyMask + ); + + w.gc = XCreateGC(w.display, w.xwindow, 0, &values); + if (!w.gc) { + suwaui_destroy_window(&w); + fprintf(stderr, "gc create fail\n"); + return (SuwaWindow){0}; + } + + w.visual = *DefaultVisual(w.display, w.screen); + + w.colormap = XCreateColormap(w.display, w.xwindow, + &w.visual, AllocNone); + if (w.colormap == None) { + suwaui_destroy_window(&w); + fprintf(stderr, "calor map create fail\n"); + return (SuwaWindow){0}; + } + + w.font = XftFontOpenName(w.display, w.screen, font); + if (!w.font) { + suwaui_destroy_window(&w); + fprintf(stderr, "font create fail\n"); + return (SuwaWindow){0}; + } + + if (!XftColorAllocName(w.display, &w.visual, + w.colormap, fg_color, &w.color)) { + suwaui_destroy_window(&w); + fprintf(stderr, "color estimate fail\n"); + return (SuwaWindow){0}; + } + + return w; +} + +__attribute__((unused)) static void suwaui_destroy_window(SuwaWindow *w) { + if (w->font) XftFontClose(w->display, w->font); + + if (w->gc) XFreeGC(w->display, w->gc); + if (w->backbuf) { + XFreePixmap(w->display, w->backbuf); + w->backbuf = None; + } + if (w->xwindow) XDestroyWindow(w->display, w->xwindow); + if (w->display) XCloseDisplay(w->display); +} +#endif // SUWAUI_IMPLEMENTS_SUWAWINDOW + +#if defined(SUWAUI_IMPLEMENTS_SUWATABS) +typedef struct { + int x, y, width, height; +} SuwaTab; + +typedef struct { + int x, y, width, height; +} SuwaTabs; +#endif // SUWAUI_IMPLEMENTS_SUWATABS + +#if defined(SUWAUI_IMPLEMENTS_SUWABUTTON) +typedef struct { + int x, y, width, height; + const char *text; + XftFont *font; + int bg_color; + XftColor fg_color; + int pressed; // 0 = 普通、1 = 押している +} SuwaButton; + +// __attribute__((unused)) static void suwaui_del_button(SuwaWindow *window, SuwaButton *button); + +__attribute__((unused)) static XftColor suwaui_set_button_fgcolor(SuwaWindow *window, const char *color) { + XftColor xcolor; + XftColorAllocName(window->display, + DefaultVisual(window->display, DefaultScreen(window->display)), + DefaultColormap(window->display, DefaultScreen(window->display)), + color, &xcolor); + + return xcolor; +} + +__attribute__((unused)) static void suwaui_draw_button(SuwaWindow *window, SuwaButton *btn, XftDraw *xftdraw) { + unsigned long curbg = btn->pressed ? BTSEL : btn->bg_color; + XSetForeground(window->display, window->gc, curbg); + XFillRectangle(window->display, window->target, window->gc, btn->x, btn->y, + btn->width, btn->height); + + // 文字の中央に + if (btn->text && window->font && xftdraw) { + const FcChar8 *str = (const FcChar8 *)btn->text; + int len = strlen((const char *)str); + + XGlyphInfo extents; + XftTextExtentsUtf8(window->display, window->font, str, len, &extents); + + int text_w = extents.xOff; + int text_h = window->font->ascent + window->font->descent; + + int tx = btn->x + (btn->width - text_w) / 2; + int ty = btn->y + (btn->height - text_h) / 2 + window->font->ascent; + + XftDrawStringUtf8(xftdraw, &btn->fg_color, window->font, tx, ty, + (FcChar8 *)btn->text, len); + } +} + +__attribute__((unused)) static SuwaButton suwaui_add_button(int x, int y, int width, int height, + const char *text, XftFont *font, int bg_color, XftColor fg_color) { + SuwaButton button = { + .x = x, + .y = y, + .width = width, + .height = height, + .text = text, + .font = font, + .bg_color = bg_color, + .fg_color = fg_color, + .pressed = 0 + }; + + return button; +} +#endif // SUWAUI_IMPLEMENTS_SUWABUTTON + +#if defined(SUWAUI_IMPLEMENTS_SUWALABEL) +typedef struct { + int x, y, width, height; + char text[64]; + XftFont *font; + XftColor fg_color; +} SuwaLabel; + +__attribute__((unused)) static void suwaui_del_label(SuwaWindow *window, SuwaLabel *label); + +__attribute__((unused)) static SuwaLabel suwaui_add_label(SuwaWindow *window, + int x, int y, int width, int height, char text[64], const char *font, + const char *fg_color, int rtl) { + (void)width; + (void)height; + SuwaLabel label = { + .x = x, + .y = y, + .width = width, + .height = height + }; + + label.font = XftFontOpenName(window->display, window->screen, font); + if (!label.font) { + fprintf(stderr, "font create fail\n"); + return (SuwaLabel){0}; + } + + strncpy(label.text, text ? text : "", sizeof(label.text) - 1); + label.text[sizeof(label.text)-1] = '\0'; + + const FcChar8 *txt = (const FcChar8 *)label.text; + int len = strlen((const char *)txt); + + XGlyphInfo extents; + XftTextExtentsUtf8(window->display, label.font, txt, len, &extents); + + if (rtl > 0) { + XWindowAttributes attr; + XGetWindowAttributes(window->display, window->xwindow, &attr); + int w = attr.width; + label.x = w - label.x - extents.xOff; + } + + if (!XftColorAllocName(window->display, + DefaultVisual(window->display, DefaultScreen(window->display)), + DefaultColormap(window->display, DefaultScreen(window->display)), + fg_color, &label.fg_color)) { + suwaui_del_label(window, &label); + fprintf(stderr, "color estimate fail\n"); + return (SuwaLabel){0}; + } + + return label; +} + +__attribute__((unused)) static void suwaui_del_label(SuwaWindow *window, SuwaLabel *label) { + if (label->font) XftFontClose(window->display, label->font); +} +#endif // SUWAUI_IMPLEMENTS_SUWALABEL + +#if defined(__cplusplus) +} +#endif diff --git a/main.c b/main.c index 93eca57..440be97 100644 --- a/main.c +++ b/main.c @@ -1,243 +1,79 @@ #include #include -#include "src/utils.h" -#include "src/control.h" +#include "control.h" const char *sofname = "ucalc"; const char *version = "0.0.0"; const char *disname = "Unix Calc"; int main() { - UiSystem ui; - ui.isrunning = 1; - ui.window.width = 382; - ui.window.height = 534; - XEvent event; - int screen; - XGCValues values; - - ui.display = XOpenDisplay(NULL); - if (ui.display == NULL) { - fprintf(stderr, "画面を開けられません。\n"); - exit(1); - } - - screen = DefaultScreen(ui.display); - - int sw = DisplayWidth(ui.display, screen); - int sh = DisplayHeight(ui.display, screen); - ui.window.x = (sw - ui.window.width) / 3; - ui.window.y = (sh - ui.window.height) / 2; - - ui.xwindow = XCreateSimpleWindow(ui.display, - RootWindow(ui.display, screen), - ui.window.x, ui.window.y, - ui.window.width, ui.window.height, - 1, BTCOL, BGCOL); - if (!ui.xwindow) { - cleanup(&ui); - fprintf(stderr, "ウィンドウを作成に失敗。\n"); - exit(1); - } - - XSizeHints *sizeHint = XAllocSizeHints(); - if (sizeHint) { - sizeHint->flags = PMinSize; - sizeHint->min_width = 382; - sizeHint->min_height = 534; - XSetWMNormalHints(ui.display, ui.xwindow, sizeHint); - XFree(sizeHint); - } - - ui.backbuf = XCreatePixmap(ui.display, ui.xwindow, - ui.window.width, ui.window.height, - DefaultDepth(ui.display, screen)); - ui.target = ui.backbuf; - - Atom net_wm_window_type = XInternAtom(ui.display, "_NET_WM_WINDOW_TYPE", False); - Atom dialog = XInternAtom(ui.display, "_NET_WM_WINDOW_TYPE_DIALOG", False); - - XChangeProperty(ui.display, ui.xwindow, net_wm_window_type, XA_ATOM, 32, - PropModeReplace, (unsigned char *)&dialog, 1); - - XStoreName(ui.display, ui.xwindow, disname); - Atom net_wm_name = XInternAtom(ui.display, "_NET_WM_NAME", False); - char displayname[16]; - snprintf(displayname, 16, "%s %s", disname, version); - XChangeProperty(ui.display, ui.xwindow, net_wm_name, - XInternAtom(ui.display, "UTF8_STRING", False), 8, - PropModeReplace, (unsigned char *)displayname, strlen(displayname)); - - XClassHint *classHint = XAllocClassHint(); - if (classHint) { - classHint->res_name = "unixcalc"; - classHint->res_class = "UnixCalc"; - XSetClassHint(ui.display, ui.xwindow, classHint); - XFree(classHint); - } - - XSetWindowBackground(ui.display, ui.xwindow, BGCOL); - - XSelectInput(ui.display, ui.xwindow, - ExposureMask - | ButtonPressMask - | ButtonReleaseMask - | KeyPressMask - // | PointerMotionMask - // | ButtonMotionMask - // | StructureNotifyMask - ); - - ui.gc = XCreateGC(ui.display, ui.xwindow, 0, &values); - if (!ui.gc) { - cleanup(&ui); - fprintf(stderr, "GCを作成に失敗。\n"); - exit(1); - } - - ui.visual = *DefaultVisual(ui.display, screen); - - ui.colormap = XCreateColormap(ui.display, ui.xwindow, &ui.visual, AllocNone); - if (ui.colormap == None) { - cleanup(&ui); - fprintf(stderr, "カラーマップを作成に失敗。\n"); - exit(1); - } - - XWindowAttributes attr; - XGetWindowAttributes(ui.display, ui.xwindow, &attr); - int w = attr.width; - - { #if defined(__OpenBSD__) - ui.resLabel.font = XftFontOpenName(ui.display, screen, "Noto Sans CJK-48"); + SuwaWindow window = suwaui_create_window( + 500, 400, 382, 534, + disname, version, "unixcalc", "UnixCalc", + "Noto Sans CJK-8", "#232320", + 0, 1, 0); + SuwaLabel resLabel = suwaui_add_label(&window, 20, 180, 0, 0, "0", + "Noto Sans CJK-48", "#f1ed25", 1); + SuwaLabel problemLabel = suwaui_add_label(&window, 20, 80, 0, 0, "", + "Noto Sans CJK-12", "#b8b515", 1); #elif defined(__FreeBSD__) - ui.resLabel.font = XftFontOpenName(ui.display, screen, "Noto Sans CJK-72"); + SuwaWindow window = suwaui_create_window( + 20, 40, 382, 534, + disname, version, "unixcalc", "UnixCalc", + "Noto Sans CJK-12", "#232020", + 0, 1, 0); + SuwaLabel resLabel = suwaui_add_label(&window, 20, 180, 0, 0, "0", + "Noto Sans CJK-72", "#ee4030", 1); + SuwaLabel problemLabel = suwaui_add_label(&window, 20, 80, 0, 0, "", + "Noto Sans CJK-24", "#b61729", 1); #endif - if (!ui.resLabel.font) { - cleanup(&ui); - fprintf(stderr, "解決フォントの読み込みに失敗。\n"); - exit(1); - } - ui.resLabel.text[0] = '0'; - ui.resLabel.text[1] = '\0'; + CtrlLabels lbl = { + .res = &resLabel, + .problem = &problemLabel + }; - const FcChar8 *text = (const FcChar8 *)ui.resLabel.text; - int len = strlen((const char *)text); - - XGlyphInfo extents; - XftTextExtentsUtf8(ui.display, ui.resLabel.font, text, len, &extents); - - ui.resLabel.x = w - 20 - extents.xOff; - ui.resLabel.y = 180; - if (!XftColorAllocName(ui.display, - DefaultVisual(ui.display, DefaultScreen(ui.display)), - DefaultColormap(ui.display, DefaultScreen(ui.display)), -#if defined(__OpenBSD__) - "#f1ed25", &ui.resLabel.fg_color)) { -#elif defined(__FreeBSD__) - "#ee4030", &ui.resLabel.fg_color)) { -#endif - cleanup(&ui); - fprintf(stderr, "色の役割に失敗。\n"); - exit(1); - } - } - - { -#if defined(__OpenBSD__) - ui.problemLabel.font = XftFontOpenName(ui.display, screen, "Noto Sans CJK-12"); -#elif defined(__FreeBSD__) - ui.problemLabel.font = XftFontOpenName(ui.display, screen, "Noto Sans CJK-24"); -#endif - if (!ui.problemLabel.font) { - cleanup(&ui); - fprintf(stderr, "問題フォントの読み込みに失敗。\n"); - exit(1); - } - - ui.problemLabel.text[0] = '\0'; - - const FcChar8 *text = (const FcChar8 *)ui.problemLabel.text; - int len = strlen((const char *)text); - - XGlyphInfo extents; - XftTextExtentsUtf8(ui.display, ui.problemLabel.font, text, len, &extents); - - ui.problemLabel.x = w - 20 - extents.xOff; - ui.problemLabel.y = 80; - if (!XftColorAllocName(ui.display, - DefaultVisual(ui.display, DefaultScreen(ui.display)), - DefaultColormap(ui.display, DefaultScreen(ui.display)), -#if defined(__OpenBSD__) - "#b8b515", &ui.problemLabel.fg_color)) { -#elif defined(__FreeBSD__) - "#b61729", &ui.problemLabel.fg_color)) { -#endif - cleanup(&ui); - fprintf(stderr, "色の役割に失敗。\n"); - exit(1); - } - } - -#if defined(__OpenBSD__) - ui.font = XftFontOpenName(ui.display, screen, "Noto Sans CJK-8"); -#elif defined(__FreeBSD__) - ui.font = XftFontOpenName(ui.display, screen, "Noto Sans CJK-12"); -#endif - if (!ui.font) { - cleanup(&ui); - fprintf(stderr, "フォントの読み込みに失敗。\n"); - exit(1); - } - -#if defined(__OpenBSD__) - if (!XftColorAllocName(ui.display, &ui.visual, ui.colormap, "#232320", &ui.color)) { -#elif defined(__FreeBSD__) - if (!XftColorAllocName(ui.display, &ui.visual, ui.colormap, "#232020", &ui.color)) { -#endif - cleanup(&ui); - fprintf(stderr, "色の役割に失敗。\n"); - exit(1); - } - - XMapWindow(ui.display, ui.xwindow); + XMapWindow(window.display, window.xwindow); { XWindowAttributes attr; - XGetWindowAttributes(ui.display, ui.xwindow, &attr); + XGetWindowAttributes(window.display, window.xwindow, &attr); XEvent fake = { .type = Expose }; - fake.xexpose.window = ui.xwindow; + fake.xexpose.window = window.xwindow; fake.xexpose.width = attr.width; fake.xexpose.height = attr.height; - control_expose(&ui, &event); + control_expose(&window, &lbl); } - while (ui.isrunning) { - XNextEvent(ui.display, &event); + while (window.isrunning) { + XNextEvent(window.display, &window.event); - switch (event.type) { + switch (window.event.type) { case Expose: case ConfigureNotify: - XClearWindow(ui.display, ui.xwindow); - control_expose(&ui, &event); + XClearWindow(window.display, window.xwindow); + control_expose(&window, &lbl); break; case ButtonPress: - if (event.xbutton.button == Button1) { - handle_button_press(&ui, event.xbutton.x, event.xbutton.y); + if (window.event.xbutton.button == Button1) { + int x = window.event.xbutton.x; + int y = window.event.xbutton.y; + handle_button_press(&window, x, y); break; } case ButtonRelease: - if (event.xbutton.button == Button1) { - handle_button_release(&ui, event.xbutton.x, event.xbutton.y); + if (window.event.xbutton.button == Button1) { + int x = window.event.xbutton.x; + int y = window.event.xbutton.y; + handle_button_release(&window, &lbl, x, y); break; } case KeyPress: - handle_key_press(&ui, &event); + handle_key_press(&window, &lbl); break; // case MotionNotify: - // handle_mouse_hover(&ui, &event); + // handle_mouse_hover(&window); // break; case ClientMessage: // WM_DELETE_WINDOW @@ -245,7 +81,9 @@ int main() { } } - cleanup(&ui); + suwaui_del_label(&window, &problemLabel); + suwaui_del_label(&window, &resLabel); + suwaui_destroy_window(&window); return 0; } diff --git a/src/control.h b/src/control.h deleted file mode 100644 index c8d430a..0000000 --- a/src/control.h +++ /dev/null @@ -1,9 +0,0 @@ -#pragma once - -#include "ui.h" - -void control_expose(UiSystem *ui, XEvent *event); -void handle_button_press(UiSystem *ui, int mx, int my); -void handle_button_release(UiSystem *ui, int mx, int my); -void handle_key_press(UiSystem *ui, XEvent *event); -void handle_mouse_hover(UiSystem *ui, XEvent *event); diff --git a/src/display.c b/src/display.c deleted file mode 100644 index 1ef288d..0000000 --- a/src/display.c +++ /dev/null @@ -1,28 +0,0 @@ -#include "display.h" - -#include - -void drawbuttons(UiSystem *ui, SuwaButton *btn, XftDraw *xftdraw) { - unsigned long curbg = btn->pressed ? BTSEL : btn->bg_color; - XSetForeground(ui->display, ui->gc, curbg); - XFillRectangle(ui->display, ui->target, ui->gc, btn->x, btn->y, - btn->width, btn->height); - - // 文字の中央に - if (btn->text && ui->font && xftdraw) { - const FcChar8 *str = (const FcChar8 *)btn->text; - int len = strlen((const char *)str); - - XGlyphInfo extents; - XftTextExtentsUtf8(ui->display, ui->font, str, len, &extents); - - int text_w = extents.xOff; - int text_h = ui->font->ascent + ui->font->descent; - - int tx = btn->x + (btn->width - text_w) / 2; - int ty = btn->y + (btn->height - text_h) / 2 + ui->font->ascent; - - XftDrawStringUtf8(xftdraw, &btn->fg_color, ui->font, tx, ty, - (FcChar8 *)btn->text, len); - } -} diff --git a/src/display.h b/src/display.h deleted file mode 100644 index c8dc5d9..0000000 --- a/src/display.h +++ /dev/null @@ -1,5 +0,0 @@ -#pragma once - -#include "ui.h" - -void drawbuttons(UiSystem *ui, SuwaButton *btn, XftDraw *xftdraw); diff --git a/src/ui.h b/src/ui.h deleted file mode 100644 index e1c9f30..0000000 --- a/src/ui.h +++ /dev/null @@ -1,53 +0,0 @@ -#pragma once - -#include -#include - -#define FGCOL 0xfcfcfc -#if defined(__OpenBSD__) -#define BGCOL 0x232320 -#define BTSEL 0xb8b515 -#define BTCOL 0xf1ed25 -#define BTHVR 0xecea71 -#elif defined(__FreeBSD__) -#define BGCOL 0x232020 -#define BTSEL 0xb61729 -#define BTCOL 0xee4030 -#define BTHVR 0xf35869 -#endif - -typedef struct { - int x, y, width, height; -} SuwaWindow; - -typedef struct { - int x, y, width, height; - const char *text; - XftFont *font; - int bg_color; - XftColor fg_color; - int pressed; // 0 = 普通、1 = 押している -} SuwaButton; - -typedef struct { - int x, y, width, height; - char text[64]; - XftFont *font; - XftColor fg_color; -} SuwaLabel; - -typedef struct { - int isrunning; - Display *display; - Window xwindow; - Drawable target; - GC gc; - Visual visual; - XftColor color; - XftFont *font; - Colormap colormap; - Pixmap backbuf; - SuwaWindow window; - SuwaLabel resLabel; - SuwaLabel problemLabel; -} UiSystem; diff --git a/src/utils.c b/src/utils.c deleted file mode 100644 index d8676c8..0000000 --- a/src/utils.c +++ /dev/null @@ -1,17 +0,0 @@ -#include "utils.h" - -void cleanup(UiSystem *ui) { - // フォント - if (ui->resLabel.font) XftFontClose(ui->display, ui->resLabel.font); - if (ui->problemLabel.font) XftFontClose(ui->display, ui->problemLabel.font); - if (ui->font) XftFontClose(ui->display, ui->font); - - // その他 - if (ui->gc) XFreeGC(ui->display, ui->gc); - if (ui->backbuf) { - XFreePixmap(ui->display, ui->backbuf); - ui->backbuf = None; - } - if (ui->xwindow) XDestroyWindow(ui->display, ui->xwindow); - if (ui->display) XCloseDisplay(ui->display); -} diff --git a/src/utils.h b/src/utils.h deleted file mode 100644 index 33faf00..0000000 --- a/src/utils.h +++ /dev/null @@ -1,5 +0,0 @@ -#pragma once - -#include "ui.h" - -void cleanup(UiSystem *ui);