it werks!
authorJohn Tsiombikas <nuclear@member.fsf.org>
Sat, 11 Jan 2020 13:28:28 +0000 (15:28 +0200)
committerJohn Tsiombikas <nuclear@member.fsf.org>
Sat, 11 Jan 2020 13:28:28 +0000 (15:28 +0200)
src/main.c
src/rawdisk.c
src/rawdisk.h
src/widgets.c
src/widgets.h

index 5f4de5d..3717955 100644 (file)
@@ -1,47 +1,69 @@
 #include <stdio.h>
 #include <stdlib.h>
 #include <ctype.h>
+#include <malloc.h>
 #include <windows.h>
+#include <winioctl.h>
 #include "widgets.h"
 #include "rawdisk.h"
 
+#define IMG_FNAME      "disk.img"
+
+static int instimg(const char *devpath);
+static void update_disks(void);
+static void onclick(struct wgt_widget *w);
+static void onmodify(struct wgt_widget *w);
+static void ck_usbonly_handler(struct wgt_widget *w);
+
+
 static struct wgt_window *win;
-static struct wgt_widget *lb_instto, *bn_inst, *bn_cancel, *cb_devs, *ck_usbonly;
+static struct wgt_widget *lb_instto, *bn_inst, *bn_cancel, *cb_devs, *ck_usbonly, *bn_cancel_inst;
 
 static struct rawdisk_device rawdev[64];
 static const char *items[64];
 static int num_rawdev;
 
-
-static void update_disks(void);
-static void onclick(struct wgt_widget *w);
-static void onmodify(struct wgt_widget *w);
-static void ck_usbonly_handler(struct wgt_widget *w);
+static FILE *imgfile;
+static long imgfile_size;
+static const char *err_noimg_fmt =
+       "Failed to open 256boss disk image: " IMG_FNAME "\n"
+       "%s\n"
+       "\n"
+       "Please run this installer from the 256boss distribution directory";
 
 
 int WINAPI WinMain(HINSTANCE hinst, HINSTANCE hprevinst, char *cmdline, int showcmd)
 {
        int x, y;
        MSG msg;
+       char *msgbuf, *errmsg;
 
+       if(!(imgfile = fopen(IMG_FNAME, "rb"))) {
+               errmsg = strerror(errno);
+               msgbuf = alloca(strlen(err_noimg_fmt) + strlen(errmsg) + 1);
+               sprintf(msgbuf, err_noimg_fmt, errmsg);
+               MessageBox(0, msgbuf, "Missing disk image!", MB_OK | MB_ICONSTOP | MB_APPLMODAL);
+               return 1;
+       }
+       fseek(imgfile, 0, SEEK_END);
+       imgfile_size = ftell(imgfile);
 
-       if(!(win = wgt_window("256boss USB stick installer", 400, 300))) {
+       if(!(win = wgt_window("256boss USB stick installer", 420, 170))) {
+               fclose(imgfile);
                return 1;
        }
-       lb_instto = wgt_label(win, "Install to device:", 10, 10);
+       lb_instto = wgt_label(win, "Install to device:", 10, 20);
 
        x = wgt_xpos_after(lb_instto, WGT_AUTO);
-       cb_devs = wgt_combo(win, items, num_rawdev, 0, x, 10, 200, WGT_AUTO, onmodify);
+       cb_devs = wgt_combo(win, items, num_rawdev, 0, x, 20, 270, 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, ck_usbonly_handler);
 
-       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);
+       y = wgt_ypos_after(ck_usbonly, 32);
+       bn_inst = wgt_button(win, "Install", 70, y, 100, WGT_AUTO, onclick);
+       bn_cancel = wgt_button(win, "Cancel", 230, y, 100, WGT_AUTO, onclick);
 
        update_disks();
 
@@ -50,10 +72,133 @@ int WINAPI WinMain(HINSTANCE hinst, HINSTANCE hprevinst, char *cmdline, int show
                DispatchMessage(&msg);
        }
 
-       wgt_destroy_window(win);
+       fclose(imgfile);
+       wgt_free_window(win);
        return 0;
 }
 
+#define WIN_XPAD       10
+#define WIN_YPAD       10
+#define MIN_BS         (4 * 1024 * 1024)
+
+static int instimg(const char *devpath)
+{
+       MSG msg;
+       HANDLE hdev;
+       DWORD wrcount;
+       DISK_GEOMETRY geom;
+       int width, max_width, height, y, sz, blksz = 512, status = -1;
+       char *blkbuf;
+       struct wgt_window *win_inst;
+       struct wgt_widget *w, *pbar, *ptext;
+       struct wgt_rect *rect;
+       long num_blk_done, total_blk;
+       int progr;
+
+       if(!(hdev = CreateFile(devpath, GENERIC_WRITE, FILE_SHARE_READ | FILE_SHARE_WRITE,
+                       0, OPEN_EXISTING, FILE_FLAG_WRITE_THROUGH, 0))) {
+               return -1;
+       }
+
+       if(rawdisk_eject(hdev) == 0) {
+               rawdisk_load(hdev);
+       }
+
+       if(DeviceIoControl(hdev, IOCTL_DISK_GET_DRIVE_GEOMETRY, 0, 0, &geom, sizeof geom, &wrcount, 0)) {
+               blksz = geom.BytesPerSector;
+       }
+
+       while(blksz < MIN_BS) blksz <<= 1;
+
+       num_blk_done = 0;
+       total_blk = (imgfile_size + blksz - 1) / blksz;
+
+       if(!(blkbuf = malloc(blksz))) {
+               CloseHandle(hdev);
+               return -1;
+       }
+
+       max_width = 0;
+
+       win_inst = wgt_window("Installing ...", 320, 140);
+       wgt_quit_on_close(win_inst, 0);
+       
+       y = WIN_YPAD;
+       sprintf(blkbuf, "Device sector size: %d bytes", (int)geom.BytesPerSector);
+       w = wgt_label(win_inst, blkbuf, WIN_XPAD, y);
+       if((width = wgt_xpos_after(w, WIN_XPAD)) > max_width) max_width = width;
+
+       y = wgt_ypos_after(w, 8);
+       sprintf(blkbuf, "Copying in blocks of: %d bytes", blksz);
+       w = wgt_label(win_inst, blkbuf, WIN_XPAD, y);
+       if((width = wgt_xpos_after(w, WIN_XPAD)) > max_width) max_width = width;
+       
+       y = wgt_ypos_after(w, 8);
+       sprintf(blkbuf, "Image size: %ld bytes (%ld blocks)", imgfile_size, total_blk);
+       w = wgt_label(win_inst, blkbuf, WIN_XPAD, y);
+       if((width = wgt_xpos_after(w, WIN_XPAD)) > max_width) max_width = width;
+       
+       y = wgt_ypos_after(w, 8);
+       ptext = wgt_label(win_inst, "-/- (0%)", WIN_XPAD, y);
+       
+       y = wgt_ypos_after(ptext, 8);
+       pbar = wgt_progbar(win_inst, WIN_XPAD, y, width - WIN_XPAD * 2, WGT_AUTO, 0);
+       if((width = wgt_xpos_after(pbar, WIN_XPAD)) > max_width) max_width = width;
+
+       y = wgt_ypos_after(pbar, 8);
+       bn_cancel_inst = wgt_button(win_inst, "Cancel", 100, y, WGT_AUTO, WGT_AUTO, onclick);
+       rect = wgt_widget_rect(bn_cancel_inst, 0);
+       wgt_move_widget(bn_cancel_inst, WIN_XPAD + (width - rect->width) / 2, rect->y);
+
+       height = wgt_ypos_after(bn_cancel_inst, WIN_YPAD);
+
+       max_width += GetSystemMetrics(SM_CXFIXEDFRAME) * 2;
+       height += GetSystemMetrics(SM_CYFIXEDFRAME) * 2 + GetSystemMetrics(SM_CYCAPTION);
+       wgt_resize_window(win_inst, max_width, height);
+
+       /* go through any pending messages once first, to allow the window to get drawn correctly */
+       while(PeekMessage(&msg, 0, 0, 0, PM_REMOVE)) {
+               if(msg.message == WM_CLOSE || msg.message == WM_DESTROY) {
+                       goto end;
+               }
+               TranslateMessage(&msg);
+               DispatchMessage(&msg);
+       }
+
+       rewind(imgfile);
+       while((sz = fread(blkbuf, 1, blksz, imgfile)) > 0) {
+               if(sz < blksz) {
+                       memset(blkbuf + sz, 0, blksz - sz);
+               }
+               SetFilePointer(hdev, num_blk_done * blksz, 0, FILE_BEGIN);
+               WriteFile(hdev, blkbuf, blksz, &wrcount, 0);
+
+               num_blk_done++;
+               progr = num_blk_done * 100 / total_blk;
+               sprintf(blkbuf, "%ld/%ld (%d%%)", num_blk_done, total_blk, progr);
+               wgt_set_text(ptext, blkbuf);
+               wgt_set_progress(pbar, progr);
+
+               while(PeekMessage(&msg, 0, 0, 0, PM_REMOVE)) {
+                       if(msg.message == WM_CLOSE || msg.message == WM_DESTROY) {
+                               goto end;
+                       }
+                       TranslateMessage(&msg);
+                       DispatchMessage(&msg);
+               }
+       }
+
+       rawdisk_refresh(hdev);
+
+       status = 0;
+end:
+       wgt_free_window(win_inst);
+
+       free(blkbuf);
+       CloseHandle(hdev);
+       return status;
+}
+
 static int isusbdev(struct rawdisk_device *dev)
 {
        char *ptr = dev->path;
@@ -86,11 +231,14 @@ static void update_disks(void)
        }
 
        if(wgt_checkbox_checked(ck_usbonly)) {
-               for(i=0; i<num_rawdev; i++) {
+               i = 0;
+               while(i < num_rawdev) {
                        if(!isusbdev(rawdev + i)) {
                                free(rawdev[i].name);
                                free(rawdev[i].path);
                                rawdev[i] = rawdev[--num_rawdev];
+                       } else {
+                               i++;
                        }
                }
        }
@@ -110,14 +258,59 @@ static void update_disks(void)
        }
 }
 
+static const char *verify_fmt = \
+       "You are about to install 256boss to: %s\n"
+       "\n"
+       "Device path:\n"
+       "%s\n"
+       "\n"
+       "Any existing data in the selected device will be lost!\n"
+       "Please verify you have selected the intended device.\n"
+       "\n"
+       "Are you certain you wish to proceed with the installation?\n";
+
+static const char *success_str = \
+       "Installation complete!\n"
+       "\n"
+       "If the 256boss partition doesn't show up immediately,\n"
+       "please eject and re-attach the device\n";
+
 static void onclick(struct wgt_widget *w)
 {
+       int sel, len;
+       unsigned int flags;
+       char *msgbuf;
+       static int inst_running;
+
        if(w == bn_inst) {
-               MessageBox(0, "clicked", "clicked", MB_OK);
+               if(inst_running) return;
+
+               sel = wgt_combo_selected(cb_devs);
+               if(sel < 0 || sel >= num_rawdev) {
+                       return;
+               }
+
+               len = strlen(verify_fmt) + strlen(rawdev[sel].name) + strlen(rawdev[sel].path);
+               msgbuf = alloca(len + 1);
+               sprintf(msgbuf, verify_fmt, rawdev[sel].name, rawdev[sel].path);
+
+               flags = MB_YESNO | MB_ICONWARNING | MB_APPLMODAL | MB_SETFOREGROUND;
+               if(MessageBox(0, msgbuf, "Proceed with installation?", flags) == IDYES) {
+                       inst_running = 1;
+                       if(instimg(rawdev[sel].path) == -1) {
+                               MessageBox(0, "Installation failed", 0, MB_OK | MB_ICONSTOP | MB_APPLMODAL | MB_SETFOREGROUND);
+                       } else {
+                               MessageBox(0, success_str, "Done", MB_OK | MB_ICONASTERISK | MB_APPLMODAL | MB_SETFOREGROUND);
+                       }
+                       inst_running = 0;
+               }
+
        } else if(w == bn_cancel) {
                PostQuitMessage(0);
-       } else if(w == ck_usbonly) {
-               update_disks();
+
+       } else if(w == bn_cancel_inst) {
+               struct wgt_window *win = wgt_widget_window(w);
+               PostMessage(wgt_window_handle(win), WM_CLOSE, 0, 0);
        }
 }
 
@@ -134,3 +327,9 @@ static void ck_usbonly_handler(struct wgt_widget *w)
 {
        update_disks();
 }
+
+static int close_action(struct wgt_window *w)
+{
+       if(w != win) return 0;
+       return 1;
+}
\ No newline at end of file
index 559f61f..ba861dc 100644 (file)
@@ -1,16 +1,21 @@
 #include <stdio.h>
 #include <stdlib.h>
 #include <string.h>
+#include <malloc.h>
 #include <windows.h>
 #include <setupapi.h>
 #include <devguid.h>
 #include <winioctl.h>
 #include "rawdisk.h"
 
-/*#define DBG_FAKE_DISKS*/
+#undef DBG_FAKE_DISKS
 
 static GUID guid_iface_disk = {0x53f56307, 0xb6bf, 0x11d0, {0x94, 0xf2, 0x00, 0xa0, 0xc9, 0x1e, 0xfb, 0x8b}};
 
+#ifndef IOCTL_DISK_UPDATE_PROPERTIES
+#define IOCTL_DISK_UPDATE_PROPERTIES CTL_CODE(IOCTL_DISK_BASE,0x0050,METHOD_BUFFERED,FILE_ANY_ACCESS)
+#endif
+
 int rawdisk_detect(struct rawdisk_device *disks, int max_disks)
 {
        int devidx, ifidx, count;
@@ -95,3 +100,29 @@ int rawdisk_detect(struct rawdisk_device *disks, int max_disks)
 
        return count;
 }
+
+int rawdisk_eject(HANDLE hdev)
+{
+       DWORD sz;
+       return DeviceIoControl(hdev, IOCTL_STORAGE_EJECT_MEDIA, 0, 0, 0, 0, &sz, 0) == 0 ? -1 : 0;
+}
+
+int rawdisk_load(HANDLE hdev)
+{
+       DWORD sz;
+       return DeviceIoControl(hdev, IOCTL_STORAGE_LOAD_MEDIA, 0, 0, 0, 0, &sz, 0) == 0 ? -1 : 0;
+}
+
+void rawdisk_refresh(HANDLE hdev)
+{
+       DWORD sz;
+       char *buf;
+
+       /* try to rock the boat so hopefully windows will re-read the disk and it'll show up */
+       DeviceIoControl(hdev, IOCTL_DISK_UPDATE_PROPERTIES, 0, 0, 0, 0, &sz, 0);
+
+       sz = sizeof(DRIVE_LAYOUT_INFORMATION) + sizeof(PARTITION_INFORMATION) * 32;
+       buf = alloca(sz);
+
+       DeviceIoControl(hdev, IOCTL_DISK_GET_DRIVE_LAYOUT, 0, 0, buf, sz, &sz, 0);
+}
\ No newline at end of file
index 5c843b1..797fb4a 100644 (file)
@@ -1,6 +1,8 @@
 #ifndef RAWDISK_H_
 #define RAWDISK_H_
 
+#include <windows.h>
+
 struct rawdisk_device {
        char *name;
        char *path;
@@ -8,4 +10,9 @@ struct rawdisk_device {
 
 int rawdisk_detect(struct rawdisk_device *disks, int max_disks);
 
+int rawdisk_eject(HANDLE hdev);
+int rawdisk_load(HANDLE hdev);
+
+void rawdisk_refresh(HANDLE hdev);
+
 #endif /* RAWDISK_H_ */
\ No newline at end of file
index 2d37973..1b97637 100644 (file)
@@ -3,6 +3,7 @@
 #include <string.h>
 #include <assert.h>
 #include <windows.h>
+#include <commctrl.h>
 #include "widgets.h"
 
 struct wgt_window {
@@ -12,6 +13,9 @@ struct wgt_window {
        HDC dc;
        char cname[32];
        struct wgt_widget *wlist;
+
+       int close_quit;
+       int (*cb_close)(struct wgt_window*);
 };
 
 struct wgt_widget {
@@ -34,6 +38,28 @@ struct wgt_widget {
 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);
 
+
+void *wgt_window_handle(struct wgt_window *win)
+{
+       return win->handle;
+}
+
+void *wgt_widget_handle(struct wgt_widget *w)
+{
+       return w->handle;
+}
+
+unsigned int wgt_window_xid(struct wgt_window *win)
+{
+       return 0;
+}
+
+unsigned int wgt_widget_xid(struct wgt_widget *w)
+{
+       return 0;
+}
+
+
 struct wgt_window *wgt_window(const char *title, int width, int height)
 {
        struct wgt_window *win;
@@ -43,11 +69,11 @@ struct wgt_window *wgt_window(const char *title, int width, int height)
        DWORD err;
        RECT rect;
 
-       if(!(win = malloc(sizeof *win))) {
+       if(!(win = calloc(1, sizeof *win))) {
                fprintf(stderr, "wgl_create_window: failed to allocate window structure\n");
                return 0;
        }
-       win->wlist = 0;
+       win->close_quit = 1;    /* by default quit when the window closes */
 
        tlen = strlen(title);
        if(tlen > sizeof win->cname - 10) {
@@ -72,7 +98,7 @@ struct wgt_window *wgt_window(const char *title, int width, int height)
                return 0;
        }
 
-       if(!(win->handle = CreateWindow(win->cname, title, WS_OVERLAPPEDWINDOW,
+       if(!(win->handle = CreateWindow(win->cname, title, WS_OVERLAPPEDWINDOW & ~WS_THICKFRAME,
                        CW_USEDEFAULT, CW_USEDEFAULT, width, height, 0, 0, hinst, 0))) {
                fprintf(stderr, "wgt_create_window: failed to create window\n");
                UnregisterClass(title, hinst);
@@ -107,10 +133,16 @@ void wgt_destroy_window(struct wgt_window *win)
                free(w);
        }
 
-       ReleaseDC(win->handle, win->dc);
-       DestroyWindow(win->handle);
-       UnregisterClass(win->cname, GetModuleHandle(0));
+       if(win->handle) {
+               ReleaseDC(win->handle, win->dc);
+               DestroyWindow(win->handle);
+               UnregisterClass(win->cname, GetModuleHandle(0));
+       }
+}
 
+void wgt_free_window(struct wgt_window *win)
+{
+       wgt_destroy_window(win);
        free(win);
 }
 
@@ -120,6 +152,23 @@ void wgt_destroy_widget(struct wgt_widget *w)
        free(w->itemlist);
 }
 
+void wgt_resize_window(struct wgt_window *win, int width, int height)
+{
+       MoveWindow(win->handle, win->rect.x, win->rect.y, width, height, TRUE);
+       win->rect.width = width;
+       win->rect.height = height;
+}
+
+void wgt_quit_on_close(struct wgt_window *win, int quit)
+{
+       win->close_quit = quit;
+}
+
+void wgt_close_action(struct wgt_window *win, int (*func)(struct wgt_window*))
+{
+       win->cb_close = func;
+}
+
 void wgt_enable_widget(struct wgt_widget *w)
 {
        w->disabled = 0;
@@ -150,6 +199,20 @@ struct wgt_rect *wgt_widget_rect(struct wgt_widget *w, struct wgt_rect *rect)
        return &w->rect;
 }
 
+void wgt_move_widget(struct wgt_widget *w, int x, int y)
+{
+       MoveWindow(w->handle, x, y, w->rect.width, w->rect.height, TRUE);
+       w->rect.x = x;
+       w->rect.y = y;
+}
+
+void wgt_resize_widget(struct wgt_widget *w, int width, int height)
+{
+       MoveWindow(w->handle, w->rect.x, w->rect.y, width, height, TRUE);
+       w->rect.width = width;
+       w->rect.height = height;
+}
+
 int wgt_xpos_after(struct wgt_widget *w, int pad)
 {
        if(pad == WGT_AUTO) {
@@ -273,6 +336,7 @@ struct wgt_widget *wgt_checkbox(struct wgt_window *win, const char *text, int on
                free(w);
                return 0;
        }
+       SendMessage(w->handle, BM_SETCHECK, on ? BST_CHECKED : BST_UNCHECKED, 0);
 
        w->rect.x = x;
        w->rect.y = y;
@@ -361,6 +425,56 @@ struct wgt_widget *wgt_combo(struct wgt_window *win, const char **items, int num
        return w;
 }
 
+struct wgt_widget *wgt_progbar(struct wgt_window *win, int x, int y, int width, int height, int p)
+{
+       struct wgt_widget *w;
+       INITCOMMONCONTROLSEX commctrl;
+
+       commctrl.dwSize = sizeof commctrl;
+       commctrl.dwICC = ICC_PROGRESS_CLASS;
+       InitCommonControlsEx(&commctrl);
+
+       if(!(w = calloc(1, sizeof *w))) {
+               fprintf(stderr, "wgt_progbar: failed to allocate widget structure\n");
+               return 0;
+       }
+       w->win = win;
+
+       if(height < 0) {
+               height = GetSystemMetrics(SM_CYVSCROLL);
+       }
+
+       if(!(w->handle = CreateWindow(PROGRESS_CLASS, 0, WS_CHILD | WS_VISIBLE, x, y,
+                       width, height, win->handle, 0, GetModuleHandle(0), 0))) {
+               fprintf(stderr, "wgt_progbar: failed to create window\n");
+               free(w);
+               return 0;
+       }
+
+       /*
+       SendMessage(w->handle, PBM_SETRANGE, 0, MAKELPARAM(0, 100));
+       SendMessage(w->handle, PBM_SETSTEP, 1, 0);
+       */
+       SendMessage(w->handle, PBM_SETPOS, p, 0);
+
+       w->rect.x = x;
+       w->rect.y = y;
+       w->rect.width = width;
+       w->rect.height = height;
+
+       w->next = win->wlist;
+       win->wlist = w;
+       return w;
+}
+
+void wgt_set_text(struct wgt_widget *w, const char *text)
+{
+       SendMessage(w->handle, WM_SETTEXT, 0, (long)text);
+
+       w->rect.width = wgt_string_size(w->win, text, 0);
+       MoveWindow(w->handle, w->rect.x, w->rect.y, w->rect.width, w->rect.height, TRUE);
+}
+
 void wgt_checkbox_check(struct wgt_widget *w)
 {
        SendMessage(w->handle, BM_SETCHECK, BST_CHECKED, 0);
@@ -432,6 +546,11 @@ const char *wgt_get_combo_item(struct wgt_widget *w, int idx)
        return w->itemlist[idx];
 }
 
+void wgt_set_progress(struct wgt_widget *w, int p)
+{
+       SendMessage(w->handle, PBM_SETPOS, p, 0);
+}
+
 static LRESULT WINAPI event_handler(HWND wnd, unsigned int msg, WPARAM wparam, LPARAM lparam)
 {
        int ncode;
@@ -442,8 +561,22 @@ static LRESULT WINAPI event_handler(HWND wnd, unsigned int msg, WPARAM wparam, L
 
        switch(msg) {
        case WM_CLOSE:
+               if(!win->cb_close || win->cb_close(win)) {
+                       if(win) {
+                               int quit = win->close_quit;
+                               wgt_destroy_window(win);
+                               memset(win, 0, sizeof *win);
+                               win->close_quit = quit;
+                       } else {
+                               DestroyWindow(wnd);
+                       }
+               }
+               break;
+
        case WM_DESTROY:
-               PostQuitMessage(0);
+               if(win && win->close_quit) {
+                       PostQuitMessage(0);
+               }
                break;
 
        case WM_COMMAND:
index caf4a4e..d170966 100644 (file)
@@ -16,15 +16,31 @@ struct wgt_rect {
 
 typedef void (*wgt_callback)(struct wgt_widget*);
 
+/* returns {win32: HWND, !win32: 0} */
+void *wgt_window_handle(struct wgt_window *win);
+void *wgt_widget_handle(struct wgt_widget *w);
+/* returns {UNIX: Window, !UNIX: 0} */
+unsigned int wgt_window_xid(struct wgt_window *win);
+unsigned int wgt_widget_xid(struct wgt_widget *w);
+
+
 struct wgt_window *wgt_window(const char *title, int width, int height);
 void wgt_destroy_window(struct wgt_window *win);
+void wgt_free_window(struct wgt_window *win);  /* destroy and then free(win) */
 void wgt_destroy_widget(struct wgt_widget *w);
 
+void wgt_resize_window(struct wgt_window *win, int width, int height);
+
+void wgt_quit_on_close(struct wgt_window *win, int quit);
+void wgt_close_action(struct wgt_window *win, int (*func)(struct wgt_window*));
+
 void wgt_enable_widget(struct wgt_widget *w);
 void wgt_disable_widget(struct wgt_widget *w);
 int wgt_widget_enabled(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);
+void wgt_move_widget(struct wgt_widget *w, int x, int y);
+void wgt_resize_widget(struct wgt_widget *w, int width, int height);
 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);
@@ -36,6 +52,9 @@ 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);
+struct wgt_widget *wgt_progbar(struct wgt_window *win, int x, int y, int width, int height, int p);
+
+void wgt_set_text(struct wgt_widget *w, const char *text);
 
 void wgt_checkbox_check(struct wgt_widget *w);
 void wgt_checkbox_uncheck(struct wgt_widget *w);
@@ -45,4 +64,6 @@ int wgt_combo_setitems(struct wgt_widget *w, const char **items, int num_items);
 int wgt_combo_selected(struct wgt_widget *w);
 const char *wgt_get_combo_item(struct wgt_widget *w, int idx);
 
+void wgt_set_progress(struct wgt_widget *w, int p);
+
 #endif /* WIDGETS_H_ */