evil-qemacs/win32.c
Charlie Gordon e3c5bfd457
Remove references to global qe_state (#45)
* Remove references to global `qe_state`

Add `QEmacsState *qs` field in context and object structures:
- `QEditScreen`, `EditState` (renamed from `qe_state`), `QuitState`
- `WinWindow` (win32.c), `X11State` (x11.c), `QEArgs` (cmd line arguments)
- `CmdLineOptionDef::func_arg()`, `QEModeData`
- move `QEKeyContext` to `QEmacsState`

Pass `QEmacsState *qs` to configuration and state functions:
- all `module_xxx_init` and static `xxx_init` functions
- `QEDisplay::dpy_init()`, `qe_register_display()`, `screen_init()̀`
- `load_input_methods()`, `unload_input_methods()`
- `register_input_method()`, `find_input_method()`
- `find_resource_file()`, `open_resource_file()`
- `qe_find_current_binding()`, `qe_list_bindings()`
- `qe_register_mode()`, `qe_find_mode()`, `qe_find_mode_filename()`
- `qe_register_cmd_line_options()`, `set_user_option()`, `set_tty_charset()`
- `qe_register_commands()`, `qe_register_bindings()`, `qe_find_cmd()`
- `qe_register_completion()`, `qe_register_variables()`
- `eb_register_data_type()`, `parse_command_line()`
- `eb_trace_bytes()`, `qe_toggle_control_h()` (split from `do_toggle_control_h()`)
- `unget_key()`, `qe_grab_keys()`, `qe_ungrab_keys()`, `qe_key_process()`
- `eb_new()`, `eb_find()`, `eb_find_new()`, `eb_scratch()`, `eb_find_file()`
- `new_shell_buffer()` `macro_add_key()`, `clear_macro(), `stop_macro()`
- `check_window()`, `check_buffer()`, `qe_kill_buffer()`, `qe_get_history()`
- `qe_register_charset()`, `find_charset()`

Add error message handlers:
- add `QEDisplay::dpy_error()` method
- add `qe_cfg_error()`, use for all expression parsing errors
- pass opaque pointer to `css_error()`
- add `qe_put_error()` and `qe_dpy_error()`

Add context argument:
- always pass a non-null window pointer to `do_refresh()`, `new_help_buffer()`

Rename functions:
- `find_resource_file` -> `qe_find_resource_file`
- `open_resource_file` -> `qe_open_resource_file`
- `input_methods_init` -> `qe_input_methods_init`
- `load_input_methods` -> `qe_load_input_methods`
- `unload_input_methods` -> `qe_unload_input_methods`
- `find_input_methods` -> `qe_find_input_methods`
- `register_input_method` -> `qe_register_input_method`
- `parse_command_line` -> `qe_parse_command_line`
- `eb_trace_bytes` -> `qe_trace_bytes`
- `eb_init` -> `qe_data_init`
- `eb_count_buffers` -> `qe_count_buffers`
- `eb_get_buffer_from_index` -> `qe_get_buffer_from_index`
- `init_all_modules` -> `qe_init_all_modules`
- `exit_all_modules` -> `qe_exit_all_modules`
- `load_all_modules` -> `qe_load_all_modules`
- `maybe_set_mark` -> `do_maybe_set_mark`
- `unget_key` -> `qe_unget_key`
- `minibuffer_init` -> `qe_minibuffer_init`
- `edit_display` -> `qe_display`
- `new_yank_buffer` -> `qe_new_yank_buffer`
- `new_shell_buffer` -> `qe_new_shell_buffer`
- `shell_find_mode` -> `qe_shell_find_mode`
- `save_selection` -> `qe_save_selection`
- `register_emulation_bindings` -> `qe_register_emulation_bindings`
- `macro_add_key` -> `qe_macro_add_key`
- `clear_macro` -> `qe_clear_macro`
- `stop_macro` -> `qe_stop_macro`
- `eb_format_message` -> `qe_format_message`
- `screen_init` -> `qe_screen_init`
- `charset_jis_init` -> `qe_charset_jis_init`
- `charset_more_init` -> `qe_charset_more_init`
- `check_window` -> `qe_check_window`
- `check_buffer` -> `qe_check_buffer`
- `eb_find_file` -> `qe_find_buffer_file`
- `eb_find` -> `qe_find_buffer_name`
- `eb_new` -> `qe_new_buffer`
- `eb_find_new` -> `qe_new_buffer` with `BC_REUSE`
- `eb_scratch` -> `qe_new_buffer` with `BC_REUSE | BC_CLEAR`
- `find_charset` -> `qe_find_charset`

Add asan, msan and ubsan targets
Fix `qe_encode64` buffer length computation.
Add `qe_new_shell_buffer` flags `SF_REUSE_BUFFER` and `SF_ERASE_BUFFER`
Bumped version to 6.3.3
fix redundant typedef
fix compilation errors
2024-12-01 23:34:00 +01:00

553 lines
14 KiB
C

/*
* MS Windows driver for QEmacs
*
* Copyright (c) 2002 Fabrice Bellard.
* Copyright (c) 2002-2024 Charlie Gordon.
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
* THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
#include "qe.h"
#include <windows.h>
extern int main1(int argc, char **argv);
LRESULT CALLBACK qe_wnd_proc(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam);
static HINSTANCE _hPrev, _hInstance;
static int font_xsize;
/* state of a single window */
typedef struct WinWindow {
HWND w;
HDC hdc;
HFONT font;
QEmacsState *qs;
} WinWindow;
typedef struct QEEventQ {
QEEvent ev;
struct QEEventQ *next;
} QEEventQ;
QEEventQ *first_event, *last_event;
WinWindow win_ctx;
#define PROG_NAME "qemacs"
/* the main is there. We simulate a unix command line by parsing the
windows command line */
int PASCAL WinMain(HINSTANCE hInstance, HINSTANCE hPrevInst,
LPSTR lpszCmdLine, int nCmdShow)
{
char **argv;
int argc, count;
char *command_line, *p;
command_line = qe_malloc_array(char, sizeof(PROG_NAME) +
strlen(lpszCmdLine) + 1);
if (!command_line)
return 0;
pstrcpy(command_line, sizeof(command_line), PROG_NAME " ");
pstrcat(command_line, sizeof(command_line), lpszCmdLine);
_hPrev = hPrevInst;
_hInstance = hInstance;
/* simplistic command line parser */
p = command_line;
count = 0;
while (qe_skip_spaces((const char **)&p)) {
while (*p != '\0' && !qe_isspace(*p))
p++;
count++;
}
argv = qe_malloc_array(char *, count + 1);
if (!argv)
return 0;
argc = 0;
p = command_line;
while (qe_skip_spaces((const char **)&p)) {
argv[argc++] = p;
while (*p != '\0' && !qe_isspace(*p))
p++;
*p = '\0';
p++;
}
argv[argc] = NULL;
#if 0
{
int i;
for (i = 0; i < argc; i++) {
printf("%d: '%s'\n", i, argv[i]);
}
}
#endif
return main1(argc, argv);
}
static int win_probe(void)
{
return 1;
}
static void init_application(void)
{
WNDCLASS wc;
wc.style = 0;
wc.lpfnWndProc = qe_wnd_proc;
wc.cbClsExtra = 0;
wc.cbWndExtra = 0;
wc.hInstance = _hInstance;
wc.hIcon = LoadIcon(NULL, IDI_APPLICATION);
wc.hCursor = LoadCursor(NULL, IDC_IBEAM);
wc.hbrBackground = NULL;
wc.lpszMenuName = NULL;
wc.lpszClassName = "qemacs";
RegisterClass(&wc);
}
static int win_init(QEditScreen *s, QEmacsState *qs, int w, int h)
{
int xsize, ysize, font_ysize;
TEXTMETRIC tm;
HDC hdc;
HWND desktop_hwnd;
if (!_hPrev)
init_application();
s->priv_data = NULL;
s->media = CSS_MEDIA_SCREEN;
s->qs = qs;
win_ctx.qs = qs;
win_ctx.font = CreateFont(-12, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
FIXED_PITCH, "fixed");
/* get font metric for window size */
desktop_hwnd = GetDesktopWindow();
hdc = GetDC(desktop_hwnd);
SelectObject(hdc, win_ctx.font);
GetTextMetrics(hdc, &tm);
ReleaseDC(desktop_hwnd, hdc);
font_xsize = tm.tmAveCharWidth;
font_ysize = tm.tmHeight;
xsize = 80 * font_xsize;
ysize = 25 * font_ysize;
s->width = xsize;
s->height = ysize;
s->charset = &charset_utf8;
s->clip_x1 = 0;
s->clip_y1 = 0;
s->clip_x2 = s->width;
s->clip_y2 = s->height;
win_ctx.w = CreateWindow("qemacs", "qemacs", WS_OVERLAPPEDWINDOW,
0, 0, xsize, ysize, NULL, NULL, _hInstance, NULL);
win_ctx.hdc = GetDC(win_ctx.w);
SelectObject(win_ctx.hdc, win_ctx.font);
// SetWindowPos (win_ctx.w, NULL, 0, 0, xsize, ysize, SWP_NOMOVE);
ShowWindow(win_ctx.w, SW_SHOW);
UpdateWindow(win_ctx.w);
return 0;
}
static void win_close(QEditScreen *s)
{
ReleaseDC(win_ctx.w, win_ctx.hdc);
DestroyWindow(win_ctx.w);
DeleteObject(win_ctx.font);
}
static void win_flush(QEditScreen *s)
{
}
static int win_is_user_input_pending(QEditScreen *s)
{
/* XXX: do it */
return 0;
}
static void push_event(QEEvent *ev)
{
QEEventQ *e;
e = qe_mallocz(QEEventQ);
if (!e)
return;
e->ev = *ev;
e->next = NULL;
if (!last_event)
first_event = e;
else
last_event->next = e;
last_event = e;
}
static void push_key(int key)
{
QEEvent ev;
qe_event_clear(&ev);
ev.type = QE_KEY_EVENT;
ev.key_event.key = key;
push_event(&ev);
}
static int ignore_wchar_msg = 0;
LRESULT CALLBACK qe_wnd_proc(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam)
{
// printf("msg=%d\n", msg);
switch (msg) {
case WM_CREATE:
/* NOTE: must store them here to avoid problems in main */
win_ctx.w = hWnd;
return 0;
/* key handling */
case WM_CHAR:
if (!ignore_wchar_msg) {
push_key(wParam);
} else {
return DefWindowProc(hWnd, msg, wParam, lParam);
}
break;
case WM_SYSCHAR:
if (!ignore_wchar_msg) {
int key;
key = wParam;
if (key >= ' ' && key <= '~') {
key = KEY_META(' ') + key - ' ';
push_key(key);
break;
}
}
return DefWindowProc(hWnd, msg, wParam, lParam);
case WM_SYSKEYDOWN:
case WM_KEYDOWN:
{
unsigned int scan;
int ctrl, shift, alt;
ctrl = (GetKeyState(VK_CONTROL) & 0x8000);
shift = (GetKeyState(VK_SHIFT) & 0x8000);
alt = (GetKeyState(VK_MENU) & 0x8000);
ignore_wchar_msg = 0;
scan = (unsigned int)((lParam >> 16) & 0x1FF);
switch (scan) {
case 0x00E:
ignore_wchar_msg = 1;
push_key(KEY_DEL);
break;
case 0x039: /* space */
ignore_wchar_msg = 1;
if (!ctrl)
push_key(KEY_SPC);
else
push_key(KEY_CTRL('@'));
break;
case 0x147: /* HOME */
push_key(KEY_HOME);
break;
case 0x148: /* UP */
push_key(KEY_UP);
break;
case 0x149: /* PGUP */
push_key(KEY_PAGEUP);
break;
case 0x14B: /* LEFT */
push_key(KEY_LEFT);
break;
case 0x14D: /* RIGHT */
push_key(KEY_RIGHT);
break;
case 0x14F: /* END */
push_key(KEY_END);
break;
case 0x150: /* DOWN */
push_key(KEY_DOWN);
break;
case 0x151: /* PGDN */
push_key(KEY_PAGEDOWN);
break;
case 0x153: /* DEL */
push_key(KEY_DELETE);
break;
case 0x152: /* INSERT */
push_key(KEY_INSERT);
break;
case 0x3b: /* F1 */
push_key(KEY_F1);
break;
case 0x3c: /* F2 */
push_key(KEY_F2);
break;
case 0x3d: /* F3 */
push_key(KEY_F3);
break;
case 0x3e: /* F4 */
/* we leave Alt-F4 to close the window */
if (alt)
return DefWindowProc(hWnd, msg, wParam, lParam);
push_key(KEY_F4);
break;
case 0x3f: /* F5 */
push_key(KEY_F5);
break;
case 0x40: /* F6 */
push_key(KEY_F6);
break;
case 0x41: /* F7 */
push_key(KEY_F7);
break;
case 0x42: /* F8 */
push_key(KEY_F8);
break;
case 0x43: /* F9 */
push_key(KEY_F9);
break;
case 0x44: /* F10 */
push_key(KEY_F10);
break;
case 0x57: /* F11 */
push_key(KEY_F11);
break;
case 0x58: /* F12 */
push_key(KEY_F12);
break;
default:
return DefWindowProc(hWnd, msg, wParam, lParam);
}
}
break;
case WM_KEYUP:
ignore_wchar_msg = 0;
break;
case WM_SYSKEYUP:
ignore_wchar_msg = 0;
return DefWindowProc(hWnd, msg, wParam, lParam);
case WM_SIZE:
if (wParam != SIZE_MINIMIZED) {
QEmacsState *qs = win_ctx.qs;
QEEvent ev;
qe_event_clear(&ev);
qs->screen->width = LOWORD(lParam);
qs->screen->height = HIWORD(lParam);
ev.expose_event.type = QE_EXPOSE_EVENT;
push_event(&ev);
}
break;
case WM_PAINT:
{
PAINTSTRUCT ps;
HDC saved_hdc;
BeginPaint(win_ctx.w, &ps);
saved_hdc = win_ctx.hdc;
win_ctx.hdc = ps.hdc;
SelectObject(win_ctx.hdc, win_ctx.font);
do_refresh(qs->active_window);
EndPaint(win_ctx.w, &ps);
win_ctx.hdc = saved_hdc;
}
break;
case WM_SETFOCUS:
case WM_KILLFOCUS:
break;
default:
return DefWindowProc(hWnd, msg, wParam, lParam);
}
return 0;
}
typedef struct QEPollData QEPollData;
int get_unicode_key(QEditScreen *s, QEPollData *pd, QEEvent *ev)
{
MSG msg;
QEEventQ *e;
for (;;) {
/* check if events queued */
if (first_event != NULL) {
e = first_event;
*ev = e->ev;
first_event = e->next;
if (!first_event)
last_event = NULL;
qe_free(&e);
break;
}
/* check if message queued */
if (GetMessage(&msg, NULL, 0, 0)) {
TranslateMessage(&msg);
DispatchMessage(&msg);
}
}
return 1;
}
static void win_fill_rectangle(QEditScreen *s,
int x1, int y1, int w, int h, QEColor color)
{
RECT rc;
HBRUSH hbr;
COLORREF col;
SetRect(&rc, x1, y1, x1 + w, y1 + h);
col = RGB((color >> 16) & 0xff, (color >> 8) & 0xff, color & 0xff);
hbr = CreateSolidBrush(col);
FillRect(win_ctx.hdc, &rc, hbr);
DeleteObject(hbr);
}
static void win_xor_rectangle(QEditScreen *s,
int x1, int y1, int w, int h, QEColor color)
{
RECT rc;
HBRUSH hbr;
COLORREF col;
color = QERGB(0xff, 0xff, 0xff);
SetRect(&rc, x1, y1, x1 + w, y1 + h);
col = RGB((color >> 16) & 0xff, (color >> 8) & 0xff, color & 0xff);
hbr = CreateSolidBrush(col);
// XXX: set XOR rop?
FillRect(win_ctx.hdc, &rc, hbr);
DeleteObject(hbr);
}
static QEFont *win_open_font(QEditScreen *s, int style, int size)
{
QEFont *font;
TEXTMETRIC tm;
font = qe_mallocz(QEFont);
if (!font)
return NULL;
GetTextMetrics(win_ctx.hdc, &tm);
font->ascent = tm.tmAscent;
font->descent = tm.tmDescent;
font->priv_data = NULL;
return font;
}
static void win_close_font(QEditScreen *s, QEFont **fontp)
{
qe_free(fontp);
}
static void win_text_metrics(QEditScreen *s, QEFont *font,
QECharMetrics *metrics,
const char32_t *str, int len)
{
int i, x;
metrics->font_ascent = font->ascent;
metrics->font_descent = font->descent;
x = 0;
for (i = 0; i < len; i++)
x += font_xsize;
metrics->width = x;
}
static void win_draw_text(QEditScreen *s, QEFont *font,
int x1, int y, const char32_t *str, int len,
QEColor color)
{
int i;
WORD buf[len];
COLORREF col;
for (i = 0; i < len; i++)
buf[i] = str[i];
col = RGB((color >> 16) & 0xff, (color >> 8) & 0xff, color & 0xff);
SetTextColor(win_ctx.hdc, col);
SetBkMode(win_ctx.hdc, TRANSPARENT);
TextOutW(win_ctx.hdc, x1, y - font->ascent, buf, len);
}
static void win_set_clip(QEditScreen *s,
int x, int y, int w, int h)
{
/* nothing to do */
}
static QEDisplay win32_dpy = {
"win32", 1, 1,
win_probe,
win_init,
win_close,
win_flush,
win_is_user_input_pending,
win_fill_rectangle,
win_xor_rectangle,
win_open_font,
win_close_font,
win_text_metrics,
win_draw_text,
win_set_clip,
NULL, /* dpy_selection_activate */
NULL, /* dpy_selection_request */
NULL, /* dpy_invalidate */
NULL, /* dpy_cursor_at */
NULL, /* dpy_bmp_alloc */
NULL, /* dpy_bmp_free */
NULL, /* dpy_bmp_draw */
NULL, /* dpy_bmp_lock */
NULL, /* dpy_bmp_unlock */
NULL, /* dpy_draw_picture */
NULL, /* dpy_full_screen */
NULL, /* dpy_describe */
NULL, /* dpy_sound_bell */
NULL, /* dpy_suspend */
qe_dpy_error, /* dpy_error */
NULL, /* next */
};
static int win32_init(QEmacsState *qs)
{
return qe_register_display(qs, &win32_dpy);
}
qe_module_init(win32_init);