From 4f312c9361b1f4c63d7697e58503cd0fb8b05fff Mon Sep 17 00:00:00 2001 From: John Tsiombikas Date: Tue, 7 Jan 2020 10:05:00 +0200 Subject: [PATCH] initial commit --- .gitignore | 8 ++ Makefile | 17 +++ instimg.dsp | 113 ++++++++++++++++ instimg.dsw | 29 +++++ src/main.c | 63 +++++++++ src/widgets.c | 403 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++ src/widgets.h | 39 ++++++ 7 files changed, 672 insertions(+) create mode 100644 .gitignore create mode 100644 Makefile create mode 100644 instimg.dsp create mode 100644 instimg.dsw create mode 100644 src/main.c create mode 100644 src/widgets.c create mode 100644 src/widgets.h diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..d87ab4b --- /dev/null +++ b/.gitignore @@ -0,0 +1,8 @@ +*.o +*.d +*.swp +*.exe +*.opt +*.plg +Debug/ +Release/ diff --git a/Makefile b/Makefile new file mode 100644 index 0000000..5a60499 --- /dev/null +++ b/Makefile @@ -0,0 +1,17 @@ +src = $(wildcard src/*.c) +obj = $(src:.c=.o) +bin = instimg.exe + +CFLAGS = -pedantic -Wall -g +LDFLAGS = -lgdi32 -lsetupapi -luuid + +ifneq ($(shell uname -s | sed 's/MINGW.*/mingw/'), mingw) + CC = i686-w64-mingw32-gcc +endif + +$(bin): $(obj) + $(CC) -o $(bin) $(obj) $(LDFLAGS) + +.PHONY: clean +clean: + rm -f $(obj) $(bin) diff --git a/instimg.dsp b/instimg.dsp new file mode 100644 index 0000000..65018c2 --- /dev/null +++ b/instimg.dsp @@ -0,0 +1,113 @@ +# Microsoft Developer Studio Project File - Name="instimg" - Package Owner=<4> +# Microsoft Developer Studio Generated Build File, Format Version 6.00 +# ** DO NOT EDIT ** + +# TARGTYPE "Win32 (x86) Application" 0x0101 + +CFG=instimg - Win32 Debug +!MESSAGE This is not a valid makefile. To build this project using NMAKE, +!MESSAGE use the Export Makefile command and run +!MESSAGE +!MESSAGE NMAKE /f "instimg.mak". +!MESSAGE +!MESSAGE You can specify a configuration when running NMAKE +!MESSAGE by defining the macro CFG on the command line. For example: +!MESSAGE +!MESSAGE NMAKE /f "instimg.mak" CFG="instimg - Win32 Debug" +!MESSAGE +!MESSAGE Possible choices for configuration are: +!MESSAGE +!MESSAGE "instimg - Win32 Release" (based on "Win32 (x86) Application") +!MESSAGE "instimg - Win32 Debug" (based on "Win32 (x86) Application") +!MESSAGE + +# Begin Project +# PROP AllowPerConfigDependencies 0 +# PROP Scc_ProjName "" +# PROP Scc_LocalPath "" +CPP=cl.exe +MTL=midl.exe +RSC=rc.exe + +!IF "$(CFG)" == "instimg - Win32 Release" + +# PROP BASE Use_MFC 0 +# PROP BASE Use_Debug_Libraries 0 +# PROP BASE Output_Dir "Release" +# PROP BASE Intermediate_Dir "Release" +# PROP BASE Target_Dir "" +# PROP Use_MFC 0 +# PROP Use_Debug_Libraries 0 +# PROP Output_Dir "Release" +# PROP Intermediate_Dir "Release" +# PROP Target_Dir "" +# ADD BASE CPP /nologo /W3 /GX /O2 /D "WIN32" /D "NDEBUG" /D "_WINDOWS" /D "_MBCS" /YX /FD /c +# ADD CPP /nologo /W3 /GX /O2 /D "WIN32" /D "NDEBUG" /D "_WINDOWS" /D "_MBCS" /YX /FD /c +# ADD BASE MTL /nologo /D "NDEBUG" /mktyplib203 /win32 +# ADD MTL /nologo /D "NDEBUG" /mktyplib203 /win32 +# ADD BASE RSC /l 0x409 /d "NDEBUG" +# ADD RSC /l 0x409 /d "NDEBUG" +BSC32=bscmake.exe +# ADD BASE BSC32 /nologo +# ADD BSC32 /nologo +LINK32=link.exe +# ADD BASE LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /subsystem:windows /machine:I386 +# ADD LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /subsystem:windows /machine:I386 + +!ELSEIF "$(CFG)" == "instimg - Win32 Debug" + +# PROP BASE Use_MFC 0 +# PROP BASE Use_Debug_Libraries 1 +# PROP BASE Output_Dir "Debug" +# PROP BASE Intermediate_Dir "Debug" +# PROP BASE Target_Dir "" +# PROP Use_MFC 0 +# PROP Use_Debug_Libraries 1 +# PROP Output_Dir "Debug" +# PROP Intermediate_Dir "Debug" +# PROP Target_Dir "" +# ADD BASE CPP /nologo /W3 /Gm /GX /ZI /Od /D "WIN32" /D "_DEBUG" /D "_WINDOWS" /D "_MBCS" /YX /FD /GZ /c +# ADD CPP /nologo /W3 /Gm /GX /ZI /Od /D "WIN32" /D "_DEBUG" /D "_WINDOWS" /D "_MBCS" /YX /FD /GZ /c +# ADD BASE MTL /nologo /D "_DEBUG" /mktyplib203 /win32 +# ADD MTL /nologo /D "_DEBUG" /mktyplib203 /win32 +# ADD BASE RSC /l 0x409 /d "_DEBUG" +# ADD RSC /l 0x409 /d "_DEBUG" +BSC32=bscmake.exe +# ADD BASE BSC32 /nologo +# ADD BSC32 /nologo +LINK32=link.exe +# ADD BASE LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /subsystem:windows /debug /machine:I386 /pdbtype:sept +# ADD LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /subsystem:windows /debug /machine:I386 /pdbtype:sept + +!ENDIF + +# Begin Target + +# Name "instimg - Win32 Release" +# Name "instimg - Win32 Debug" +# Begin Group "Source Files" + +# PROP Default_Filter "cpp;c;cxx;rc;def;r;odl;idl;hpj;bat" +# Begin Source File + +SOURCE=.\src\main.c +# End Source File +# Begin Source File + +SOURCE=.\src\widgets.c +# End Source File +# End Group +# Begin Group "Header Files" + +# PROP Default_Filter "h;hpp;hxx;hm;inl" +# Begin Source File + +SOURCE=.\src\widgets.h +# End Source File +# End Group +# Begin Group "Resource Files" + +# PROP Default_Filter "ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe" +# End Group +# End Target +# End Project diff --git a/instimg.dsw b/instimg.dsw new file mode 100644 index 0000000..86b2e56 --- /dev/null +++ b/instimg.dsw @@ -0,0 +1,29 @@ +Microsoft Developer Studio Workspace File, Format Version 6.00 +# WARNING: DO NOT EDIT OR DELETE THIS WORKSPACE FILE! + +############################################################################### + +Project: "instimg"=.\instimg.dsp - Package Owner=<4> + +Package=<5> +{{{ +}}} + +Package=<4> +{{{ +}}} + +############################################################################### + +Global: + +Package=<5> +{{{ +}}} + +Package=<3> +{{{ +}}} + +############################################################################### + diff --git a/src/main.c b/src/main.c new file mode 100644 index 0000000..43a5b62 --- /dev/null +++ b/src/main.c @@ -0,0 +1,63 @@ +#include +#include +#include +#include "widgets.h" + +static struct wgt_window *win; +static struct wgt_widget *lb_instto, *bn_inst, *bn_cancel, *cb_devs, *ck_usbonly; + +static void onclick(struct wgt_widget *w); +static void onmodify(struct wgt_widget *w); + +int WINAPI WinMain(HINSTANCE hinst, HINSTANCE hprevinst, char *cmdline, int showcmd) +{ + int x, y; + MSG msg; + static const char *items[] = { + "item one", + "item two", + "item three" + }; + + + if(!(win = wgt_window("256boss USB stick installer", 400, 300))) { + return 1; + } + lb_instto = wgt_label(win, "Install to device:", 10, 10); + + x = wgt_xpos_after(lb_instto, WGT_AUTO); + cb_devs = wgt_combo(win, items, sizeof items / sizeof *items, 0, + x, 10, WGT_AUTO, WGT_AUTO, onmodify); + + y = wgt_ypos_after(cb_devs, 16); + ck_usbonly = wgt_checkbox(win, "only show USB devices", 1, x, y, + WGT_AUTO, WGT_AUTO, 0); + + y = wgt_ypos_after(ck_usbonly, 16); + bn_inst = wgt_button(win, "Install", 10, y, WGT_AUTO, WGT_AUTO, onclick); + + x = wgt_xpos_after(bn_inst, WGT_AUTO); + bn_cancel = wgt_button(win, "Cancel", x, y, WGT_AUTO, WGT_AUTO, onclick); + + while(GetMessage(&msg, 0, 0, 0)) { + TranslateMessage(&msg); + DispatchMessage(&msg); + } + + wgt_destroy_window(win); + return 0; +} + +static void onclick(struct wgt_widget *w) +{ + MessageBox(0, "clicked", "clicked", MB_OK); +} + +static void onmodify(struct wgt_widget *w) +{ + int sel = wgt_combo_selected(w); + if(sel >= 0) { + const char *selstr = wgt_get_combo_item(w, sel); + MessageBox(0, selstr, "selected item", MB_OK); + } +} \ No newline at end of file diff --git a/src/widgets.c b/src/widgets.c new file mode 100644 index 0000000..7796cba --- /dev/null +++ b/src/widgets.c @@ -0,0 +1,403 @@ +#include +#include +#include +#include +#include +#include "widgets.h" + +struct wgt_window { + HWND handle; + struct wgt_rect rect; + + HDC dc; + char cname[32]; + struct wgt_widget *wlist; +}; + +struct wgt_widget { + HWND handle; + struct wgt_rect rect; + + struct wgt_window *win; + struct wgt_widget *next; + + char **itemlist; + int num_items; + + wgt_callback cb_click; + wgt_callback cb_modify; +}; + + +static LRESULT WINAPI event_handler(HWND win, unsigned int msg, WPARAM wparam, LPARAM lparam); +static struct wgt_widget *find_widget(struct wgt_window *win, HWND handle); + +struct wgt_window *wgt_window(const char *title, int width, int height) +{ + struct wgt_window *win; + int tlen; + WNDCLASS wc; + HINSTANCE hinst; + DWORD err; + RECT rect; + + if(!(win = malloc(sizeof *win))) { + fprintf(stderr, "wgl_create_window: failed to allocate window structure\n"); + return 0; + } + win->wlist = 0; + + tlen = strlen(title); + if(tlen > sizeof win->cname - 10) { + tlen = sizeof win->cname - 10; + } + strcpy(win->cname, "WGTCLASS-"); + memcpy(win->cname + 9, title, tlen); + win->cname[tlen + 9] = 0; + + hinst = GetModuleHandle(0); + + memset(&wc, 0, sizeof wc); + wc.hInstance = hinst; + wc.hbrBackground = (HBRUSH)GetStockObject(LTGRAY_BRUSH); + wc.hCursor = LoadCursor(0, IDC_ARROW); + wc.hIcon = LoadIcon(0, IDI_APPLICATION); + wc.lpszClassName = win->cname; + wc.lpfnWndProc = event_handler; + wc.cbWndExtra = 4; + if(!RegisterClass(&wc)) { + fprintf(stderr, "wgt_create_window: failed to register window class\n"); + return 0; + } + + if(!(win->handle = CreateWindow(win->cname, title, WS_OVERLAPPEDWINDOW, + CW_USEDEFAULT, CW_USEDEFAULT, width, height, 0, 0, hinst, 0))) { + fprintf(stderr, "wgt_create_window: failed to create window\n"); + UnregisterClass(title, hinst); + return 0; + } + win->dc = GetDC(win->handle); + SetLastError(0); + if(!SetWindowLong(win->handle, GWL_USERDATA, (long)win) && (err = GetLastError())) { + fprintf(stderr, "wgt_create_window: failed to store window user data (err: %d)\n", err); + UnregisterClass(title, hinst); + DestroyWindow(win->handle); + return 0; + } + ShowWindow(win->handle, 1); + + GetWindowRect(win->handle, &rect); + + win->rect.x = rect.left; + win->rect.y = rect.top; + win->rect.width = rect.right - rect.left; + win->rect.height = rect.bottom - rect.top; + + return win; +} + +void wgt_destroy_window(struct wgt_window *win) +{ + while(win->wlist) { + struct wgt_widget *w = win->wlist; + win->wlist = win->wlist->next; + wgt_destroy_widget(w); + free(w); + } + + ReleaseDC(win->handle, win->dc); + DestroyWindow(win->handle); + UnregisterClass(win->cname, GetModuleHandle(0)); + + free(win); +} + +void wgt_destroy_widget(struct wgt_widget *w) +{ + DestroyWindow(w->handle); + free(w->itemlist); +} + +struct wgt_window *wgt_widget_window(struct wgt_widget *w) +{ + return w->win; +} + +struct wgt_rect *wgt_widget_rect(struct wgt_widget *w, struct wgt_rect *rect) +{ + if(rect) { + *rect = w->rect; + } + return &w->rect; +} + +int wgt_xpos_after(struct wgt_widget *w, int pad) +{ + if(pad == WGT_AUTO) { + pad = 8; + } + return w->rect.x + w->rect.width + pad; +} + +int wgt_ypos_after(struct wgt_widget *w, int pad) +{ + if(pad == WGT_AUTO) { + pad = 8; + } + return w->rect.y + w->rect.height + pad; +} + +int wgt_string_size(struct wgt_window *win, const char *s, struct wgt_rect *rect) +{ + SIZE sz; + + GetTextExtentPoint32(win->dc, (char*)s, strlen(s), &sz); + if(rect) { + rect->x = rect->y = 0; + rect->width = sz.cx; + rect->height = sz.cy; + } + return sz.cx; +} + + +struct wgt_widget *wgt_label(struct wgt_window *win, const char *text, int x, int y) +{ + struct wgt_widget *w; + struct wgt_rect textsz; + + if(!(w = calloc(1, sizeof *w))) { + fprintf(stderr, "wgt_label: failed to allocate widget structure\n"); + return 0; + } + w->win = win; + + wgt_string_size(win, text, &textsz); + + if(!(w->handle = CreateWindow("STATIC", text, WS_CHILD | WS_VISIBLE | SS_SIMPLE, + x, y, textsz.width, textsz.height, win->handle, 0, GetModuleHandle(0), 0))) { + fprintf(stderr, "wgt_label: failed to create window\n"); + free(w); + return 0; + } + + w->rect.x = x; + w->rect.y = y; + w->rect.width = textsz.width; + w->rect.height = textsz.height; + + w->next = win->wlist; + win->wlist = w; + return w; +} + +struct wgt_widget *wgt_button(struct wgt_window *win, const char *text, int x, int y, + int width, int height, wgt_callback clickfunc) +{ + struct wgt_widget *w; + struct wgt_rect textsz; + + if(!(w = calloc(1, sizeof *w))) { + fprintf(stderr, "wgt_button: failed to allocate widget structure\n"); + return 0; + } + w->win = win; + + if(width < 0 || height < 0) { + wgt_string_size(win, text, &textsz); + if(width < 0) width = textsz.width * 5 / 3; + if(height < 0) height = textsz.height * 10 / 7; + } + + if(!(w->handle = CreateWindow("BUTTON", text, WS_CHILD | WS_VISIBLE | BS_PUSHBUTTON, + x, y, width, height, win->handle, 0, GetModuleHandle(0), 0))) { + fprintf(stderr, "wgt_button: failed to create window\n"); + free(w); + return 0; + } + + w->rect.x = x; + w->rect.y = y; + w->rect.width = width; + w->rect.height = height; + + w->cb_click = clickfunc; + w->next = win->wlist; + win->wlist = w; + return w; +} + +struct wgt_widget *wgt_checkbox(struct wgt_window *win, const char *text, int on, + int x, int y, int width, int height, wgt_callback modfunc) +{ + struct wgt_widget *w; + struct wgt_rect textsz; + + if(!(w = calloc(1, sizeof *w))) { + fprintf(stderr, "wgt_checkbox: failed to allocate widget structure\n"); + return 0; + } + w->win = win; + + if(width < 0 || height < 0) { + wgt_string_size(win, text, &textsz); + if(width < 0) width = textsz.width + 32; /* XXX */ + if(height < 0) height = textsz.height; + } + + if(!(w->handle = CreateWindow("BUTTON", text, WS_CHILD | WS_VISIBLE | BS_AUTOCHECKBOX, + x, y, width, height, win->handle, 0, GetModuleHandle(0), 0))) { + fprintf(stderr, "wgt_checkbox: failed to create window\n"); + free(w); + return 0; + } + + w->rect.x = x; + w->rect.y = y; + w->rect.width = width; + w->rect.height = height; + + w->cb_modify = modfunc; + w->next = win->wlist; + win->wlist = w; + return w; +} + +struct wgt_widget *wgt_combo(struct wgt_window *win, const char **items, int num_items, + int sel, int x, int y, int width, int height, wgt_callback modfunc) +{ + int i, max_width, max_height, sum_height, res; + struct wgt_widget *w; + struct wgt_rect textsz; + + if(!(w = calloc(1, sizeof *w))) { + fprintf(stderr, "wgt_combo: failed to allocate widget structure\n"); + return 0; + } + w->win = win; + + if(!(w->itemlist = malloc(num_items * sizeof *w->itemlist))) { + fprintf(stderr, "wgt_combo: failed to allocate item list\n"); + free(w); + return 0; + } + + max_width = max_height = sum_height = 0; + + for(i=0; i max_width) max_width = textsz.width; + if(textsz.height > max_height) max_height = textsz.height; + sum_height += textsz.height; + } + if(width < 0) width = max_width * 3 / 2; + if(height < 0) height = sum_height * 2; + + + if(!(w->handle = CreateWindow("COMBOBOX", items[0], + WS_CHILD | WS_VISIBLE | CBS_DROPDOWNLIST | CBS_HASSTRINGS, + x, y, width, height, win->handle, 0, GetModuleHandle(0), 0))) { + fprintf(stderr, "wgt_combo: failed to create window\n"); + free(w); + return 0; + } + + for(i=0; iitemlist[i] = strdup(items[i]))) { + fprintf(stderr, "wgt_combo: failed to allocate item\n"); + while(--i >= 0) free(w->itemlist[i]); + DestroyWindow(w->handle); + free(w->itemlist); + free(w); + return 0; + } + if((res = SendMessage(w->handle, CB_ADDSTRING, 0, (long)items[i])) != i) { + fprintf(stderr, "wgt_combo: failed to add item\n"); + } + } + w->num_items = num_items; + + if(sel < 0) sel = 0; + if(sel >= num_items) sel = num_items - 1; + SendMessage(w->handle, CB_SETCURSEL, sel, 0); + + w->rect.x = x; + w->rect.y = y; + w->rect.width = width; + w->rect.height = max_height; + + w->cb_modify = modfunc; + w->next = win->wlist; + win->wlist = w; + return w; +} + +int wgt_combo_selected(struct wgt_widget *w) +{ + int sel = SendMessage(w->handle, CB_GETCURSEL, 0, 0); + if(sel == CB_ERR) return -1; + return sel; +} + +const char *wgt_get_combo_item(struct wgt_widget *w, int idx) +{ + if(idx < 0 || idx >= w->num_items) { + return 0; + } + return w->itemlist[idx]; +} + +static LRESULT WINAPI event_handler(HWND wnd, unsigned int msg, WPARAM wparam, LPARAM lparam) +{ + int ncode; + struct wgt_widget *w; + struct wgt_window *win; + + win = (struct wgt_window*)GetWindowLong(wnd, GWL_USERDATA); + + switch(msg) { + case WM_CLOSE: + case WM_DESTROY: + PostQuitMessage(0); + break; + + case WM_COMMAND: + if(!win || !(w = find_widget(win, (HWND)lparam))) { + break; + } + ncode = HIWORD(wparam); + + switch(ncode) { + case BN_CLICKED: + if(w->cb_click) { + w->cb_click(w); + } + break; + + case CBN_SELCHANGE: + if(w->cb_modify) { + w->cb_modify(w); + } + break; + + default: + break; + } + + default: + return DefWindowProc(wnd, msg, wparam, lparam); + } + return 0; +} + +static struct wgt_widget *find_widget(struct wgt_window *win, HWND handle) +{ + struct wgt_widget *w; + + w = win->wlist; + while(w) { + if(w->handle == handle) return w; + w = w->next; + } + return 0; +} \ No newline at end of file diff --git a/src/widgets.h b/src/widgets.h new file mode 100644 index 0000000..a81da8f --- /dev/null +++ b/src/widgets.h @@ -0,0 +1,39 @@ +#ifndef WIDGETS_H_ +#define WIDGETS_H_ + +#include + +#define WGT_AUTO (-0x4242) + +struct wgt_window; +struct wgt_widget; + +struct wgt_rect { + int x, y; + int width, height; +}; + +typedef void (*wgt_callback)(struct wgt_widget*); + +struct wgt_window *wgt_window(const char *title, int width, int height); +void wgt_destroy_window(struct wgt_window *win); +void wgt_destroy_widget(struct wgt_widget *w); + +struct wgt_window *wgt_widget_window(struct wgt_widget *w); +struct wgt_rect *wgt_widget_rect(struct wgt_widget *w, struct wgt_rect *rect); +int wgt_xpos_after(struct wgt_widget *w, int pad); +int wgt_ypos_after(struct wgt_widget *w, int pad); +int wgt_string_size(struct wgt_window *win, const char *s, struct wgt_rect *rect); + +struct wgt_widget *wgt_label(struct wgt_window *win, const char *text, int x, int y); +struct wgt_widget *wgt_button(struct wgt_window *win, const char *text, int x, int y, + int width, int height, wgt_callback clickfunc); +struct wgt_widget *wgt_checkbox(struct wgt_window *win, const char *text, int on, + int x, int y, int width, int height, wgt_callback modfunc); +struct wgt_widget *wgt_combo(struct wgt_window *win, const char **items, int num_items, + int sel, int x, int y, int width, int height, wgt_callback modfunc); + +int wgt_combo_selected(struct wgt_widget *w); +const char *wgt_get_combo_item(struct wgt_widget *w, int idx); + +#endif /* WIDGETS_H_ */ \ No newline at end of file -- 1.7.10.4