aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--config.h2
-rw-r--r--main.c325
-rw-r--r--options.c12
-rw-r--r--options.h1
-rw-r--r--thumbs.c117
-rw-r--r--thumbs.h51
-rw-r--r--util.h3
-rw-r--r--window.c42
-rw-r--r--window.h7
9 files changed, 412 insertions, 148 deletions
diff --git a/config.h b/config.h
index e140446..b53acb4 100644
--- a/config.h
+++ b/config.h
@@ -19,3 +19,5 @@ static const float zoom_levels[] = {
12.5, 25.0, 50.0, 75.0,
100.0, 150.0, 200.0, 400.0, 800.0
};
+
+#define THUMB_SIZE 50
diff --git a/main.c b/main.c
index bbbeb59..ab2508f 100644
--- a/main.c
+++ b/main.c
@@ -22,6 +22,7 @@
#include <dirent.h>
#include <sys/select.h>
#include <sys/stat.h>
+#include <sys/time.h>
#include <X11/Xlib.h>
#include <X11/Xutil.h>
@@ -29,15 +30,23 @@
#include "image.h"
#include "options.h"
+#include "thumbs.h"
#include "util.h"
#include "window.h"
+typedef enum appmode_e {
+ MODE_NORMAL = 0,
+ MODE_THUMBS
+} appmode_t;
+
void update_title();
int check_append(const char*);
void read_dir_rec(const char*);
void run();
+appmode_t mode;
img_t img;
+tns_t tns;
win_t win;
#define DNAME_CNT 512
@@ -46,6 +55,8 @@ const char **filenames;
int filecnt, fileidx;
size_t filesize;
+int tns_loaded;
+
#define TITLE_LEN 256
char win_title[TITLE_LEN];
@@ -54,6 +65,7 @@ void cleanup() {
if (!in++) {
img_free(&img);
+ tns_free(&tns, &win);
win_close(&win);
}
}
@@ -119,12 +131,23 @@ int main(int argc, char **argv) {
win_open(&win);
img_init(&img, &win);
- load_image();
- img_render(&img, &win);
- update_title();
+ if (options->thumbnails) {
+ tns_loaded = 0;
+ tns_init(&tns, filecnt);
+ }
- run();
+ if (options->thumbnails == 2) {
+ mode = MODE_THUMBS;
+ win_clear(&win);
+ win_draw(&win);
+ } else {
+ mode = MODE_NORMAL;
+ load_image();
+ img_render(&img, &win);
+ }
+ update_title();
+ run();
cleanup();
return 0;
@@ -135,15 +158,21 @@ void update_title() {
float size;
const char *unit;
- if (img.valid) {
- size = filesize;
- size_readable(&size, &unit);
- n = snprintf(win_title, TITLE_LEN, "sxiv: [%d/%d] <%d%%> (%.2f%s) %s",
- fileidx + 1, filecnt, (int) (img.zoom * 100.0), size, unit,
- filenames[fileidx]);
+ if (mode == MODE_THUMBS) {
+ n = snprintf(win_title, TITLE_LEN, "sxiv: [%d/%d] %s",
+ tns.cnt ? tns.sel + 1 : 0, tns.cnt,
+ tns.cnt ? tns.thumbs[tns.sel].filename : "");
} else {
- n = snprintf(win_title, TITLE_LEN, "sxiv: [%d/%d] broken: %s",
- fileidx + 1, filecnt, filenames[fileidx]);
+ if (img.valid) {
+ size = filesize;
+ size_readable(&size, &unit);
+ n = snprintf(win_title, TITLE_LEN, "sxiv: [%d/%d] <%d%%> (%.2f%s) %s",
+ fileidx + 1, filecnt, (int) (img.zoom * 100.0), size, unit,
+ filenames[fileidx]);
+ } else {
+ n = snprintf(win_title, TITLE_LEN, "sxiv: [%d/%d] broken: %s",
+ fileidx + 1, filecnt, filenames[fileidx]);
+ }
}
if (n >= TITLE_LEN) {
@@ -233,6 +262,15 @@ void read_dir_rec(const char *dirname) {
unsigned char timeout;
int mox, moy;
+void redraw() {
+ if (mode == MODE_NORMAL)
+ img_render(&img, &win);
+ else
+ tns_render(&tns, &win);
+ update_title();
+ timeout = 0;
+}
+
void on_keypress(XKeyEvent *kev) {
int x, y;
unsigned int w, h;
@@ -246,6 +284,117 @@ void on_keypress(XKeyEvent *kev) {
XLookupString(kev, &key, 1, &ksym, NULL);
changed = 0;
+ if (mode == MODE_NORMAL) {
+ switch (ksym) {
+ /* navigate image list */
+ case XK_n:
+ case XK_space:
+ if (fileidx + 1 < filecnt) {
+ ++fileidx;
+ changed = load_image();
+ }
+ break;
+ case XK_p:
+ case XK_BackSpace:
+ if (fileidx > 0) {
+ --fileidx;
+ changed = load_image();
+ }
+ break;
+ case XK_bracketleft:
+ if (fileidx != 0) {
+ fileidx = MAX(0, fileidx - 10);
+ changed = load_image();
+ }
+ break;
+ case XK_bracketright:
+ if (fileidx != filecnt - 1) {
+ fileidx = MIN(fileidx + 10, filecnt - 1);
+ changed = load_image();
+ }
+ break;
+ case XK_g:
+ if (fileidx != 0) {
+ fileidx = 0;
+ changed = load_image();
+ }
+ break;
+ case XK_G:
+ if (fileidx != filecnt - 1) {
+ fileidx = filecnt - 1;
+ changed = load_image();
+ }
+ break;
+
+ /* zooming */
+ case XK_plus:
+ case XK_equal:
+ changed = img_zoom_in(&img);
+ break;
+ case XK_minus:
+ changed = img_zoom_out(&img);
+ break;
+ case XK_0:
+ changed = img_zoom(&img, 1.0);
+ break;
+ case XK_w:
+ if ((changed = img_fit_win(&img, &win)))
+ img_center(&img, &win);
+ break;
+
+ /* panning */
+ case XK_h:
+ case XK_Left:
+ changed = img_pan(&img, &win, PAN_LEFT);
+ break;
+ case XK_j:
+ case XK_Down:
+ changed = img_pan(&img, &win, PAN_DOWN);
+ break;
+ case XK_k:
+ case XK_Up:
+ changed = img_pan(&img, &win, PAN_UP);
+ break;
+ case XK_l:
+ case XK_Right:
+ changed = img_pan(&img, &win, PAN_RIGHT);
+ break;
+
+ /* rotation */
+ case XK_less:
+ img_rotate_left(&img, &win);
+ changed = 1;
+ break;
+ case XK_greater:
+ img_rotate_right(&img, &win);
+ changed = 1;
+ break;
+
+ /* control window */
+ case XK_W:
+ x = win.x + img.x;
+ y = win.y + img.y;
+ w = img.w * img.zoom;
+ h = img.h * img.zoom;
+ if ((changed = win_moveresize(&win, x, y, w, h))) {
+ img.x = x - win.x;
+ img.y = y - win.y;
+ }
+ break;
+
+ /* miscellaneous */
+ case XK_a:
+ img_toggle_antialias(&img);
+ changed = 1;
+ break;
+ case XK_r:
+ changed = load_image();
+ break;
+ }
+ } else {
+ }
+
+ /* common key mappings */
switch (ksym) {
case XK_Escape:
cleanup();
@@ -253,122 +402,14 @@ void on_keypress(XKeyEvent *kev) {
case XK_q:
cleanup();
exit(0);
-
- /* navigate image list */
- case XK_n:
- case XK_space:
- if (fileidx + 1 < filecnt) {
- ++fileidx;
- changed = load_image();
- }
- break;
- case XK_p:
- case XK_BackSpace:
- if (fileidx > 0) {
- --fileidx;
- changed = load_image();
- }
- break;
- case XK_bracketleft:
- if (fileidx != 0) {
- fileidx = MAX(0, fileidx - 10);
- changed = load_image();
- }
- break;
- case XK_bracketright:
- if (fileidx != filecnt - 1) {
- fileidx = MIN(fileidx + 10, filecnt - 1);
- changed = load_image();
- }
- break;
- case XK_g:
- if (fileidx != 0) {
- fileidx = 0;
- changed = load_image();
- }
- break;
- case XK_G:
- if (fileidx != filecnt - 1) {
- fileidx = filecnt - 1;
- changed = load_image();
- }
- break;
-
- /* zooming */
- case XK_plus:
- case XK_equal:
- changed = img_zoom_in(&img);
- break;
- case XK_minus:
- changed = img_zoom_out(&img);
- break;
- case XK_0:
- changed = img_zoom(&img, 1.0);
- break;
- case XK_w:
- if ((changed = img_fit_win(&img, &win)))
- img_center(&img, &win);
- break;
-
- /* panning */
- case XK_h:
- case XK_Left:
- changed = img_pan(&img, &win, PAN_LEFT);
- break;
- case XK_j:
- case XK_Down:
- changed = img_pan(&img, &win, PAN_DOWN);
- break;
- case XK_k:
- case XK_Up:
- changed = img_pan(&img, &win, PAN_UP);
- break;
- case XK_l:
- case XK_Right:
- changed = img_pan(&img, &win, PAN_RIGHT);
- break;
-
- /* rotation */
- case XK_less:
- img_rotate_left(&img, &win);
- changed = 1;
- break;
- case XK_greater:
- img_rotate_right(&img, &win);
- changed = 1;
- break;
-
- /* control window */
case XK_f:
win_toggle_fullscreen(&win);
/* render on next configurenotify */
break;
- case XK_W:
- x = win.x + img.x;
- y = win.y + img.y;
- w = img.w * img.zoom;
- h = img.h * img.zoom;
- if ((changed = win_moveresize(&win, x, y, w, h))) {
- img.x = x - win.x;
- img.y = y - win.y;
- }
- break;
-
- /* miscellaneous */
- case XK_a:
- img_toggle_antialias(&img);
- changed = 1;
- break;
- case XK_r:
- changed = load_image();
- break;
}
- if (changed) {
- img_render(&img, &win);
- update_title();
- timeout = 0;
- }
+ if (changed)
+ redraw();
}
void on_buttonpress(XButtonEvent *bev) {
@@ -423,11 +464,8 @@ void on_buttonpress(XButtonEvent *bev) {
break;
}
- if (changed) {
- img_render(&img, &win);
- update_title();
- timeout = 0;
- }
+ if (changed)
+ redraw();
}
void on_motionnotify(XMotionEvent *mev) {
@@ -446,23 +484,39 @@ void on_motionnotify(XMotionEvent *mev) {
void run() {
int xfd;
fd_set fds;
- struct timeval t;
+ struct timeval t, t0;
XEvent ev;
timeout = 0;
while (1) {
- if (timeout) {
+ if (mode == MODE_THUMBS && tns_loaded < filecnt) {
+ win_set_cursor(&win, CURSOR_WATCH);
+ gettimeofday(&t0, 0);
+
+ while (!XPending(win.env.dpy) && tns_loaded < filecnt) {
+ tns_load(&tns, &win, filenames[tns_loaded++]);
+ gettimeofday(&t, 0);
+ if (TV_TO_DOUBLE(t) - TV_TO_DOUBLE(t0) >= 0.25)
+ break;
+ }
+ if (tns_loaded == filecnt)
+ win_set_cursor(&win, CURSOR_ARROW);
+ if (!XPending(win.env.dpy)) {
+ redraw();
+ continue;
+ } else {
+ timeout = 1;
+ }
+ } else if (timeout) {
t.tv_sec = 0;
t.tv_usec = 75000;
xfd = ConnectionNumber(win.env.dpy);
FD_ZERO(&fds);
FD_SET(xfd, &fds);
-
- if (!XPending(win.env.dpy) && !select(xfd + 1, &fds, 0, 0, &t)) {
- img_render(&img, &win);
- timeout = 0;
- }
+
+ if (!XPending(win.env.dpy) && !select(xfd + 1, &fds, 0, 0, &t))
+ redraw();
}
if (!XNextEvent(win.env.dpy, &ev)) {
@@ -482,8 +536,9 @@ void run() {
break;
case ConfigureNotify:
if (win_configure(&win, &ev.xconfigure)) {
- img.checkpan = 1;
timeout = 1;
+ if (mode == MODE_NORMAL)
+ img.checkpan = 1;
}
break;
case ClientMessage:
diff --git a/options.c b/options.c
index 286d0b6..ca3be0c 100644
--- a/options.c
+++ b/options.c
@@ -25,12 +25,13 @@
#include "config.h"
#include "options.h"
+#include "util.h"
options_t _options;
const options_t *options = (const options_t*) &_options;
void print_usage() {
- printf("usage: sxiv [-dFfhpqrsvZ] [-g GEOMETRY] [-z ZOOM] FILES...\n");
+ printf("usage: sxiv [-dFfhpqrsTtvZ] [-g GEOMETRY] [-z ZOOM] FILES...\n");
}
void print_version() {
@@ -44,6 +45,7 @@ void parse_options(int argc, char **argv) {
_options.scalemode = SCALE_MODE;
_options.zoom = 1.0;
_options.aa = 1;
+ _options.thumbnails = 0;
_options.fixed = 0;
_options.fullscreen = 0;
@@ -52,7 +54,7 @@ void parse_options(int argc, char **argv) {
_options.quiet = 0;
_options.recursive = 0;
- while ((opt = getopt(argc, argv, "dFfg:hpqrsvZz:")) != -1) {
+ while ((opt = getopt(argc, argv, "dFfg:hpqrsTtvZz:")) != -1) {
switch (opt) {
case '?':
print_usage();
@@ -84,6 +86,12 @@ void parse_options(int argc, char **argv) {
case 's':
_options.scalemode = SCALE_FIT;
break;
+ case 'T':
+ _options.thumbnails = 2;
+ break;
+ case 't':
+ _options.thumbnails = MAX(_options.thumbnails, 1);
+ break;
case 'v':
print_version();
exit(0);
diff --git a/options.h b/options.h
index fd2e0bd..246ddb7 100644
--- a/options.h
+++ b/options.h
@@ -29,6 +29,7 @@ typedef struct options_s {
scalemode_t scalemode;
float zoom;
unsigned char aa;
+ unsigned char thumbnails;
unsigned char fixed;
unsigned char fullscreen;
diff --git a/thumbs.c b/thumbs.c
new file mode 100644
index 0000000..bac00d9
--- /dev/null
+++ b/thumbs.c
@@ -0,0 +1,117 @@
+/* sxiv: thumbs.c
+ * Copyright (c) 2011 Bert Muennich <muennich at informatik.hu-berlin.de>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+#include <stdlib.h>
+#include <string.h>
+
+#include <Imlib2.h>
+
+#include "config.h"
+#include "thumbs.h"
+#include "util.h"
+
+const int thumb_dim = THUMB_SIZE + 10;
+
+void tns_init(tns_t *tns, int cnt) {
+ if (!tns)
+ return;
+
+ tns->cnt = tns->first = tns->sel = 0;
+ tns->thumbs = (thumb_t*) s_malloc(cnt * sizeof(thumb_t));
+}
+
+void tns_free(tns_t *tns, win_t *win) {
+ int i;
+
+ if (!tns)
+ return;
+
+ for (i = 0; i < tns->cnt; ++i)
+ win_free_pixmap(win, tns->thumbs[i].pm);
+
+ free(tns->thumbs);
+ tns->thumbs = NULL;
+}
+
+void tns_load(tns_t *tns, win_t *win, const char *filename) {
+ int w, h;
+ float z, zw, zh;
+ thumb_t *t;
+ Imlib_Image *im;
+
+ if (!tns || !win || !filename)
+ return;
+
+ if (!(im = imlib_load_image(filename)))
+ return;
+
+ imlib_context_set_image(im);
+
+ w = imlib_image_get_width();
+ h = imlib_image_get_height();
+ zw = (float) THUMB_SIZE / (float) w;
+ zh = (float) THUMB_SIZE / (float) h;
+ z = MIN(zw, zh);
+
+ t = &tns->thumbs[tns->cnt++];
+ t->filename = filename;
+ t->w = z * w;
+ t->h = z * h;
+
+ t->pm = win_create_pixmap(win, t->w, t->h);
+ imlib_context_set_drawable(t->pm);
+ imlib_render_image_part_on_drawable_at_size(0, 0, w, h,
+ 0, 0, t->w, t->h);
+ imlib_free_image();
+}
+
+void tns_render(tns_t *tns, win_t *win) {
+ int i, cnt, x, y;
+
+ if (!tns || !win)
+ return;
+
+ tns->cols = win->w / thumb_dim;
+ tns->rows = win->h / thumb_dim;
+
+ cnt = tns->cols * tns->rows;
+ if (tns->first && tns->first + cnt > tns->cnt)
+ tns->first = MAX(0, tns->cnt - cnt);
+ cnt = MIN(tns->first + cnt, tns->cnt);
+
+ win_clear(win);
+
+ x = y = 5;
+ i = tns->first;
+
+ while (i < cnt) {
+ tns->thumbs[i].x = x + (THUMB_SIZE - tns->thumbs[i].w) / 2;
+ tns->thumbs[i].y = y + (THUMB_SIZE - tns->thumbs[i].h) / 2;
+ win_draw_pixmap(win, tns->thumbs[i].pm, tns->thumbs[i].x,
+ tns->thumbs[i].y, tns->thumbs[i].w, tns->thumbs[i].h);
+ if (++i % tns->cols == 0) {
+ x = 5;
+ y += thumb_dim;
+ } else {
+ x += thumb_dim;
+ }
+ }
+
+ win_draw(win);
+}
+
diff --git a/thumbs.h b/thumbs.h
new file mode 100644
index 0000000..01b161a
--- /dev/null
+++ b/thumbs.h
@@ -0,0 +1,51 @@
+/* sxiv: thumbs.h
+ * Copyright (c) 2011 Bert Muennich <muennich at informatik.hu-berlin.de>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+#ifndef THUMBS_H
+#define THUMBS_H
+
+#include "window.h"
+
+typedef struct thumb_s {
+ Pixmap pm;
+ const char *filename;
+ int x;
+ int y;
+ int w;
+ int h;
+} thumb_t;
+
+typedef struct tns_s {
+ thumb_t *thumbs;
+ int cnt;
+ int cols;
+ int rows;
+ int first;
+ int sel;
+} tns_t;
+
+extern const int thumb_dim;
+
+void tns_init(tns_t*, int);
+void tns_free(tns_t*, win_t*);
+
+void tns_load(tns_t*, win_t*, const char*);
+
+void tns_render(tns_t*, win_t*);
+
+#endif /* THUMBS_H */
diff --git a/util.h b/util.h
index 2caf347..f1db6b8 100644
--- a/util.h
+++ b/util.h
@@ -27,6 +27,9 @@
#define MAX(a,b) ((a) > (b) ? (a) : (b))
#define LEN(a) (sizeof(a) / sizeof(a[0]))
+#define TV_TO_DOUBLE(x) ((double) ((x).tv_sec) + 0.000001 * \
+ (double) ((x).tv_usec))
+
void* s_malloc(size_t);
void* s_realloc(void*, size_t);
diff --git a/window.c b/window.c
index 76a8e87..38501ca 100644
--- a/window.c
+++ b/window.c
@@ -26,8 +26,9 @@
#include "util.h"
#include "window.h"
-static Cursor arrow;
-static Cursor hand;
+static Cursor carrow;
+static Cursor chand;
+static Cursor cwatch;
static GC bgc;
Atom wm_delete_win;
@@ -107,8 +108,9 @@ void win_open(win_t *win) {
XSelectInput(e->dpy, win->xwin, StructureNotifyMask | KeyPressMask |
ButtonPressMask | ButtonReleaseMask | Button2MotionMask);
- arrow = XCreateFontCursor(e->dpy, XC_left_ptr);
- hand = XCreateFontCursor(e->dpy, XC_fleur);
+ carrow = XCreateFontCursor(e->dpy, XC_left_ptr);
+ chand = XCreateFontCursor(e->dpy, XC_fleur);
+ cwatch = XCreateFontCursor(e->dpy, XC_watch);
bgc = XCreateGC(e->dpy, win->xwin, 0, None);
@@ -135,8 +137,9 @@ void win_close(win_t *win) {
if (!win)
return;
- XFreeCursor(win->env.dpy, arrow);
- XFreeCursor(win->env.dpy, hand);
+ XFreeCursor(win->env.dpy, carrow);
+ XFreeCursor(win->env.dpy, chand);
+ XFreeCursor(win->env.dpy, cwatch);
XFreeGC(win->env.dpy, bgc);
@@ -211,6 +214,23 @@ void win_toggle_fullscreen(win_t *win) {
SubstructureNotifyMask, &ev);
}
+Pixmap win_create_pixmap(win_t *win, int w, int h) {
+ if (!win)
+ return 0;
+
+ return XCreatePixmap(win->env.dpy, win->xwin, w, h, win->env.depth);
+}
+
+void win_free_pixmap(win_t *win, Pixmap pm) {
+ if (win && pm)
+ XFreePixmap(win->env.dpy, pm);
+}
+
+void win_draw_pixmap(win_t *win, Pixmap pm, int x, int y, int w, int h) {
+ if (win)
+ XCopyArea(win->env.dpy, pm, win->pm, bgc, 0, 0, w, h, x, y);
+}
+
void win_clear(win_t *win) {
win_env_t *e;
XGCValues gcval;
@@ -219,14 +239,13 @@ void win_clear(win_t *win) {
return;
e = &win->env;
+ gcval.foreground = win->fullscreen ? BlackPixel(e->dpy, e->scr) : win->bgcol;
if (win->pm)
XFreePixmap(e->dpy, win->pm);
win->pm = XCreatePixmap(e->dpy, win->xwin, e->scrw, e->scrh, e->depth);
- gcval.foreground = win->fullscreen ? BlackPixel(e->dpy, e->scr) : win->bgcol;
XChangeGC(e->dpy, bgc, GCForeground, &gcval);
-
XFillRectangle(e->dpy, win->pm, bgc, 0, 0, e->scrw, e->scrh);
}
@@ -264,11 +283,14 @@ void win_set_cursor(win_t *win, win_cur_t cursor) {
switch (cursor) {
case CURSOR_HAND:
- XDefineCursor(win->env.dpy, win->xwin, hand);
+ XDefineCursor(win->env.dpy, win->xwin, chand);
+ break;
+ case CURSOR_WATCH:
+ XDefineCursor(win->env.dpy, win->xwin, cwatch);
break;
case CURSOR_ARROW:
default:
- XDefineCursor(win->env.dpy, win->xwin, arrow);
+ XDefineCursor(win->env.dpy, win->xwin, carrow);
break;
}
}
diff --git a/window.h b/window.h
index 1ae431b..f00af22 100644
--- a/window.h
+++ b/window.h
@@ -25,7 +25,8 @@
typedef enum win_cur_e {
CURSOR_ARROW = 0,
- CURSOR_HAND
+ CURSOR_HAND,
+ CURSOR_WATCH
} win_cur_t;
typedef struct win_env_s {
@@ -63,6 +64,10 @@ int win_moveresize(win_t*, int, int, unsigned int, unsigned int);
void win_toggle_fullscreen(win_t*);
+Pixmap win_create_pixmap(win_t*, int, int);
+void win_free_pixmap(win_t*, Pixmap);
+void win_draw_pixmap(win_t*, Pixmap, int, int, int, int);
+
void win_clear(win_t*);
void win_draw(win_t*);