最初コミット

This commit is contained in:
2026-01-17 07:24:25 +09:00
commit b20fa6cc3a
17 changed files with 756 additions and 0 deletions

389
src/control.c Normal file
View File

@@ -0,0 +1,389 @@
#include <X11/keysym.h>
#include <math.h>
#include <ctype.h>
#include "control.h"
#include "display.h"
#include "program.h"
static const char *btn_labels[][10] = {
{ NULL }, // 数字表示
{ "C", "±", "%", "÷" },
{ "7", "8", "9", "×" },
{ "4", "5", "6", "-" },
{ "1", "2", "3", "+" },
{ "0", ".", "=", "<" },
};
#define NUM_ROWS 6
#define NUM_COLS 6
static SuwaButton *find_button_at(int mx, int my) {
static SuwaButton found = {0};
int btn_w = 93;
int btn_h = 60;
int padding = 2;
int row = -1;
int col = -1;
for (int r = 1; r < NUM_ROWS; ++r) {
int y_start = 224 + (r - 1) * btn_h + padding;
if (my >= y_start && my < y_start + btn_h) {
row = r;
break;
}
}
if (row < 0) return NULL;
for (int c = 0; c < NUM_COLS; ++c) {
int x_start = padding + c * (btn_w + padding);
if (mx >= x_start && mx < x_start + btn_w) {
col = c;
break;
}
}
if (col < 0) return NULL;
found.x = padding + col * (btn_w + padding);
found.y = 224 + (row - 1) * (btn_h + padding);
found.width = btn_w;
found.height = btn_h;
found.label = btn_labels[row][col];
found.pressed = 1;
return &found;
}
void append_to_input(char c) {
if ((unsigned long)input_pos >= sizeof(curinput) - 2) return;
strcpy(displayprb, "");
curinput[input_pos++] = c;
curinput[input_pos] = '\0';
strncpy(displaytxt, curinput, sizeof(displaytxt) - 1);
displaytxt[sizeof(displaytxt) - 1] = '\0';
}
void clear_calculator(void) {
strcpy(displayprb, "");
curinput[0] = '\0';
input_pos = 0;
strcpy(displaytxt, "0");
}
double evaluate_simple(const char *expr) {
double res = 0.0;
double cur = 0.0;
char op = '+';
int i = 0;
snprintf(displayprb, sizeof(displayprb), "%s=", expr);
while (expr[i]) {
if (expr[i] == ' ') { i++; continue; }
if (isdigit(expr[i]) || expr[i] == '.') {
char numbuf[32];
int j = 0;
while (isdigit(expr[i]) || expr[i] == '.') {
if (j < 31) numbuf[j++] = expr[i];
++i;
}
numbuf[j] = '\0';
cur = atof(numbuf);
} else {
if (op == '+') res += cur;
else if (op == '-') res -= cur;
else if (op == '*') res *= cur;
else if (op == '/') {
if (cur == 0) return NAN;
res /= cur;
}
op = expr[i];
++i;
cur = 0.0;
}
}
if (op == '+') res += cur;
else if (op == '-') res -= cur;
else if (op == '*') res *= cur;
else if (op == '/') {
if (cur == 0) return NAN;
res /= cur;
}
return res;
}
void control_expose(Display *dpy, Window wnd, GC gc, XEvent *e, XftColor *color, XftFont *f, XftFont *df, XftFont *pf) {
(void)color;
if (e->type != Expose && e->type != ConfigureNotify) return;
XWindowAttributes attr;
XGetWindowAttributes(dpy, wnd, &attr);
int w = attr.width;
int h = attr.height;
if (backbuf != None) {
XFreePixmap(dpy, backbuf);
}
backbuf = XCreatePixmap(dpy, wnd, w, h, DefaultDepth(dpy, DefaultScreen(dpy)));
if (backbuf == None) {
backbuf = wnd;
}
XftDraw *backdraw = XftDrawCreate(dpy, backbuf,
DefaultVisual(dpy, DefaultScreen(dpy)), DefaultColormap(dpy, DefaultScreen(dpy)));
if (!backdraw) {
fprintf(stderr, "Pixmap向けXftDrawの作成に失敗。\n");
XFreePixmap(dpy, backbuf);
backbuf = None;
return;
}
XSetForeground(dpy, gc, BGCOL);
XFillRectangle(dpy, backbuf, gc, 0, 0, w, h);
// 出力
if (displaytxt[0] != '\0' && df) {
const FcChar8 *text = (const FcChar8 *)displaytxt;
int len = strlen((const char *)text);
XGlyphInfo extents;
XftTextExtentsUtf8(dpy, df, text, len, &extents);
int tx = w - 20 - extents.xOff;
int ty = 160;
XftColor discol;
XftColorAllocName(dpy, DefaultVisual(dpy, DefaultScreen(dpy)),
DefaultColormap(dpy, DefaultScreen(dpy)),
"#ee4030", &discol);
XftDrawStringUtf8(backdraw, &discol, df, tx, ty, text, len);
XftColorFree(dpy, DefaultVisual(dpy, DefaultScreen(dpy)),
DefaultColormap(dpy, DefaultScreen(dpy)), &discol);
}
if (displayprb[0] != '\0' && pf) {
const FcChar8 *text = (const FcChar8 *)displayprb;
int len = strlen((const char *)text);
XGlyphInfo extents;
XftTextExtentsUtf8(dpy, pf, text, len, &extents);
int tx = w - 20 - extents.xOff;
int ty = 80;
XftColor discol;
XftColorAllocName(dpy, DefaultVisual(dpy, DefaultScreen(dpy)),
DefaultColormap(dpy, DefaultScreen(dpy)),
"#b61729", &discol);
XftDrawStringUtf8(backdraw, &discol, pf, tx, ty, text, len);
XftColorFree(dpy, DefaultVisual(dpy, DefaultScreen(dpy)),
DefaultColormap(dpy, DefaultScreen(dpy)), &discol);
}
int width = 93;
int height = 60;
int padding = 2;
printf("ウィンドウ: (%dx%d)\n", attr.width, attr.height);
for (int row = 0; row < 6; ++row) {
int y = 162 + row * (height + padding);
if (y + height > h) break;
int cols = 4;
for (int col = 0; col < cols; ++col) {
const char *label = btn_labels[row][col];
if (!label) continue;
int x = padding + col * (width + padding);
SuwaButton btn;
btn.x = x;
btn.y = y;
btn.width = width;
btn.height = height;
btn.label = label;
btn.pressed = 0;
drawbuttons(dpy, backbuf, gc, &btn, backdraw, color, f);
}
}
XCopyArea(dpy, backbuf, wnd, gc, 0, 0, w, h, 0, 0);
XftDrawDestroy(backdraw);
XFlush(dpy);
}
void handle_button_press(Display *dpy, GC gc, int mx, int my, XftColor *col, XftFont *f) {
SuwaButton *btn = find_button_at(mx, my);
if (!btn) return;
btn->pressed = 1;
XftDraw *backdraw = XftDrawCreate(dpy, backbuf,
DefaultVisual(dpy, DefaultScreen(dpy)), DefaultColormap(dpy, DefaultScreen(dpy)));
if (!backdraw) {
fprintf(stderr, "Pixmap向けXftDrawの作成に失敗。\n");
XFreePixmap(dpy, backbuf);
backbuf = None;
return;
}
drawbuttons(dpy, backbuf, gc, btn, backdraw, col, f);
XFlush(dpy);
}
void handle_button_release(Display *dpy, Window wnd, GC gc, int mx, int my, XftColor *col, XftFont *f, XftFont *df, XftFont *pf) {
SuwaButton *btn = find_button_at(mx, my);
if (!btn) return;
btn->pressed = 0;
XftDraw *backdraw = XftDrawCreate(dpy, backbuf,
DefaultVisual(dpy, DefaultScreen(dpy)), DefaultColormap(dpy, DefaultScreen(dpy)));
if (!backdraw) {
fprintf(stderr, "Pixmap向けXftDrawの作成に失敗。\n");
XFreePixmap(dpy, backbuf);
backbuf = None;
return;
}
drawbuttons(dpy, backbuf, gc, btn, backdraw, col, f);
XftDrawDestroy(backdraw);
XFlush(dpy);
const char *label = btn->label;
if (strcmp(label, "C") == 0) {
clear_calculator();
} else if (strcmp(label, "<") == 0) {
curinput[--input_pos] = '\0';
strcpy(displaytxt, curinput[0] ? curinput : "0");
} else if (strcmp(label, "=") == 0) {
double res = evaluate_simple(curinput);
if (isnan(res)) {
strncpy(displaytxt, "Error", 5);
} else {
snprintf(displaytxt, sizeof(displaytxt), "%.8g", res);
strncpy(curinput, displaytxt, strlen(displaytxt));
input_pos = strlen(curinput);
}
} else if (strlen(label) == 1) {
append_to_input(label[0]);
}
control_expose(dpy, wnd, gc, &(XEvent){.type = Expose}, col, f, df, pf);
}
void handle_key_press(Display *dpy, Window wnd, GC gc, XEvent *event, XftColor *col, XftFont *f, XftFont *df, XftFont *pf) {
KeySym keysym;
char buf[32];
int len;
(void)len;
keysym = XLookupKeysym(&event->xkey, 0);
len = XLookupString(&event->xkey, buf, sizeof(buf), &keysym, NULL);
if (keysym == XK_Q) {
isrunning = 0;
return;
}
if (keysym == XK_B) {
puts("標準電卓画面");
}
if (keysym == XK_S) {
puts("関数電卓画面");
}
if (keysym == XK_P) {
puts("プログラマー電卓画面");
}
if (keysym == XK_H) {
puts("履歴画面");
}
if (keysym == XK_h) {
puts("ヘルプ画面");
puts("-----");
puts("Q = 終了 Exit");
puts("X = 1文字の消し Remove 1 character");
puts("C = クリ了 Clear");
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();
goto redraw;
}
if (keysym == XK_Return || keysym == XK_KP_Enter) {
double res = evaluate_simple(curinput);
if (isnan(res)) {
strncpy(displaytxt, "Error", 5);
} else {
snprintf(displaytxt, sizeof(displaytxt), "%.8g", res);
}
goto redraw;
}
if ((keysym == XK_BackSpace && input_pos > 0)
|| (keysym == XK_Delete && input_pos > 0)
|| (keysym == XK_X && input_pos > 0)) {
curinput[--input_pos] = '\0';
strcpy(displaytxt, curinput[0] ? curinput : "0");
goto redraw;
}
char c = 0;
if (keysym >= XK_0 && keysym <= XK_9) c = '0' + (keysym - XK_0);
else if (keysym >= XK_KP_0 && keysym <= XK_KP_9) c = '0' + (keysym - XK_0);
else if (keysym == XK_KP_Add || keysym == XK_plus) c = '+';
else if (keysym == XK_KP_Subtract || keysym == XK_minus) c = '-';
else if (keysym == XK_KP_Multiply || keysym == XK_asterisk) c = '*';
else if (keysym == XK_KP_Divide || keysym == XK_slash) c = '/';
else if (keysym == XK_KP_Decimal || keysym == XK_period) c = '.';
if (c) {
append_to_input(c);
goto redraw;
}
return;
redraw:
control_expose(dpy, wnd, gc, &(XEvent){.type = Expose}, col, f, df, pf);
}
// void handle_mouse_hover(Display *dpy, Window wnd, GC gc, XEvent *event, XftColor *col, XftFont *f) {
// SuwaButton *hover = find_button_at(event->xmotion.x, event->xmotion.y);
// if (hover != hovered_btn) {
// if (hovered_btn) {
// hovered_btn->pressed = 0;
// redraw_single_button(dpy, wnd, gc, col, f, hovered_btn);
// }
// hovered_btn = hover;
// if (hovered_btn) {
// redraw_single_button(dpy, wnd, gc, col, f, hovered_btn);
// }
// }
// }

10
src/control.h Normal file
View File

@@ -0,0 +1,10 @@
#pragma once
#include <X11/Xlib.h>
#include <X11/Xft/Xft.h>
void control_expose(Display *dpy, Window wnd, GC gc, XEvent *event, XftColor *color, XftFont *f, XftFont *df, XftFont *pf);
void handle_button_press(Display *dpy, GC gc, int mx, int my, XftColor *col, XftFont *f);
void handle_button_release(Display *dpy, Window wnd, GC gc, int mx, int my, XftColor *col, XftFont *f, XftFont *df, XftFont *pf);
void handle_key_press(Display *dpy, Window wnd, GC gc, XEvent *event, XftColor *col, XftFont *f, XftFont *df, XftFont *pf);
void handle_mouse_hover(Display *dpy, Window wnd, GC gc, XEvent *event, XftColor *col, XftFont *f);

30
src/display.c Normal file
View File

@@ -0,0 +1,30 @@
#include "display.h"
#include "program.h"
#include <string.h>
void drawbuttons(Display *dpy, Drawable target, GC gc, SuwaButton *btn,
XftDraw *xftdraw, XftColor *textcolor, XftFont *font) {
unsigned long curbg = btn->pressed ? BTSEL : BTCOL;
XSetForeground(dpy, gc, curbg);
XFillRectangle(dpy, target, gc, btn->x, btn->y, btn->width, btn->height);
// 文字の中央に
if (btn->label && font && xftdraw) {
const FcChar8 *str = (const FcChar8 *)btn->label;
int len = strlen((const char *)str);
XGlyphInfo extents;
XftTextExtentsUtf8(dpy, font, str, len, &extents);
int text_w = extents.xOff;
int text_h = font->ascent + font->descent;
int tx = btn->x + (btn->width - text_w) / 2;
int ty = btn->y + (btn->height - text_h) / 2 + font->ascent;
// tx -= extents.x;
XftDrawStringUtf8(xftdraw, textcolor, font, tx, ty, (FcChar8 *)btn->label, len);
}
}

6
src/display.h Normal file
View File

@@ -0,0 +1,6 @@
#pragma once
#include "program.h"
void drawbuttons(Display *dpy, Drawable target, GC gc, SuwaButton *btn,
XftDraw *xftdraw, XftColor *textcolor, XftFont *font);

12
src/program.c Normal file
View File

@@ -0,0 +1,12 @@
#include "program.h"
int window_width = 382;
int window_height = 534;
int isrunning = 1;
char displayprb[128] = "";
char curinput[256] = {0};
char displaytxt[64] = "0";
int input_pos = 0;
Pixmap backbuf = None;
SuwaButton *hovered_btn = NULL;
SuwaButton *pressed_btn = NULL;

22
src/program.h Normal file
View File

@@ -0,0 +1,22 @@
#pragma once
#include <X11/Xlib.h>
#include <X11/Xft/Xft.h>
#include "ui.h"
#define FGCOL 0xfcfcfc
#define BGCOL 0x232020
#define BTSEL 0xb61729
#define BTCOL 0xee4030
#define BTHVR 0xf35869
extern int window_width;
extern int window_height;
extern int isrunning;
extern char displayprb[128];
extern char curinput[256];
extern char displaytxt[64];
extern int input_pos;
extern Pixmap backbuf;
extern SuwaButton *hovered_btn;
extern SuwaButton *pressed_btn;

7
src/ui.h Normal file
View File

@@ -0,0 +1,7 @@
#pragma once
typedef struct {
int x, y, width, height;
const char *label;
int pressed; // 0 = 普通、1 = 押している
} SuwaButton;

19
src/utils.c Normal file
View File

@@ -0,0 +1,19 @@
#include "utils.h"
void cleanup(Display *display, Window window, GC gc,
XftColor *color, XftColor *btncolor, XftFont *font,
XftFont *disfont, XftFont *prbfont, Colormap colormap, Visual *visual,
Pixmap backbuf) {
if (btncolor) XftColorFree(display, visual, colormap, btncolor);
if (color) XftColorFree(display, visual, colormap, color);
if (disfont) XftFontClose(display, disfont);
if (prbfont) XftFontClose(display, prbfont);
if (font) XftFontClose(display, font);
if (gc) XFreeGC(display, gc);
if (backbuf) {
XFreePixmap(display, backbuf);
backbuf = None;
}
if (window) XDestroyWindow(display, window);
if (display) XCloseDisplay(display);
}

8
src/utils.h Normal file
View File

@@ -0,0 +1,8 @@
#pragma once
#include "program.h"
void cleanup(Display *display, Window window, GC gc,
XftColor *color, XftColor *btncolor, XftFont *font,
XftFont *disfont, XftFont *prbfont, Colormap colormap, Visual *visual,
Pixmap backbuf);