You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 
 
 

674 lines
16 KiB

  1. #include <stdio.h>
  2. #include <stdlib.h>
  3. #include <string.h>
  4. #include <sys/types.h>
  5. #include <sys/stat.h>
  6. #include <gtk/gtk.h>
  7. #include "util.h"
  8. #include "gtk.h"
  9. #include "Game.h"
  10. #include "UI.h"
  11. struct MCursor {
  12. GdkCursor *cursor;
  13. };
  14. struct Picture {
  15. gint width, height;
  16. GdkPixmap *pix;
  17. GdkBitmap *mask;
  18. GdkGC *gc;
  19. };
  20. static const char *pictdir;
  21. static GtkWidget *toplevel, *base, *menubar, *field;
  22. static GtkWidget *dialogs[DIALOG_MAX + 1];
  23. static GtkWidget *pausebutton;
  24. static guint timer;
  25. static GdkGC *stdgc;
  26. static GdkPixmap *offscreen;
  27. static GdkFont *font;
  28. static GdkColor white, black;
  29. static int screensize;
  30. /*
  31. * Callback functions
  32. */
  33. static void
  34. gtk_ui_popup_dialog(int index) {
  35. GtkWidget *popup;
  36. int tx, ty, tw, th;
  37. int px, py, pw, ph;
  38. popup = dialogs[index];
  39. gdk_window_get_origin(toplevel->window, &tx, &ty);
  40. gdk_window_get_size(toplevel->window, &tw, &th);
  41. gdk_window_get_size(popup->window, &pw, &ph);
  42. px = tx + (tw - pw) / 2;
  43. py = ty + (th - ph) / 2;
  44. gtk_window_set_position(GTK_WINDOW(popup), GTK_WIN_POS_NONE);
  45. gtk_widget_set_uposition(popup, px, py);
  46. gtk_widget_show_all(popup);
  47. gtk_main();
  48. }
  49. static void
  50. popdown(void) {
  51. gtk_main_quit();
  52. }
  53. static void
  54. new_game(void) {
  55. Game_start(1);
  56. }
  57. static void
  58. quit_game(void) {
  59. Game_quit();
  60. }
  61. static void
  62. warp_apply(GtkWidget *text) {
  63. char *str;
  64. char *endp;
  65. int newlevel;
  66. str = gtk_entry_get_text(GTK_ENTRY(text));
  67. newlevel = strtol(str, &endp, 10);
  68. if (*endp != '\0')
  69. return;
  70. Game_warp_to_level(newlevel);
  71. }
  72. static void
  73. enter_name(GtkWidget *text) {
  74. char *str;
  75. str = gtk_entry_get_text(GTK_ENTRY(text));
  76. Game_add_high_score(str);
  77. }
  78. /*
  79. * Event handlers
  80. */
  81. static gboolean
  82. leave_window(GtkWidget *widget, GdkEvent *event, gpointer user_data) {
  83. UNUSED(widget);
  84. UNUSED(event);
  85. UNUSED(user_data);
  86. UI_pause_game();
  87. return FALSE;
  88. }
  89. static gboolean
  90. enter_window(GtkWidget *widget, GdkEvent *event, gpointer user_data) {
  91. UNUSED(widget);
  92. UNUSED(event);
  93. UNUSED(user_data);
  94. UI_resume_game();
  95. return FALSE;
  96. }
  97. static gboolean
  98. redraw_window(GtkWidget *widget, GdkEvent *event, gpointer user_data) {
  99. UNUSED(widget);
  100. UNUSED(event);
  101. UNUSED(user_data);
  102. UI_refresh();
  103. return FALSE;
  104. }
  105. static gboolean
  106. button_press(GtkWidget *widget, GdkEvent *event, gpointer user_data) {
  107. GdkEventButton *buttonevent = (GdkEventButton *) event;
  108. UNUSED(widget);
  109. UNUSED(user_data);
  110. Game_button_press((int)buttonevent->x, (int)buttonevent->y);
  111. return FALSE;
  112. }
  113. static gboolean
  114. button_release(GtkWidget *widget, GdkEvent *event, gpointer user_data) {
  115. GdkEventButton *buttonevent = (GdkEventButton *) event;
  116. UNUSED(widget);
  117. UNUSED(user_data);
  118. Game_button_release((int)buttonevent->x, (int)buttonevent->y);
  119. return FALSE;
  120. }
  121. static int
  122. timer_tick(gpointer arg) {
  123. UNUSED(arg);
  124. UI_restart_timer();
  125. Game_update();
  126. return TRUE;
  127. }
  128. /*
  129. * Cursor handling
  130. */
  131. #include "bitmaps/initfail.xbm"
  132. #include "bitmaps/arch.xbm"
  133. #include "bitmaps/bsd.xbm"
  134. #include "bitmaps/centos.xbm"
  135. #include "bitmaps/debian.xbm"
  136. #include "bitmaps/gentoo.xbm"
  137. #include "bitmaps/mandriva.xbm"
  138. #include "bitmaps/openbsd.xbm"
  139. #include "bitmaps/slackware.xbm"
  140. #include "bitmaps/suse.xbm"
  141. #include "bitmaps/ubuntu.xbm"
  142. #include "bitmaps/bucket.xbm"
  143. #include "bitmaps/hand_down.xbm"
  144. #include "bitmaps/hand_down_mask.xbm"
  145. #include "bitmaps/hand_up.xbm"
  146. #include "bitmaps/hand_up_mask.xbm"
  147. typedef struct cursormap {
  148. const char *name;
  149. int width, height;
  150. const char *data, *maskdata;
  151. } cursormap;
  152. #define CURSOR_ADD(x) \
  153. {#x, x ## _width, x ## _height, x ## _bits, NULL}
  154. #define CURSOR_ADD_MASKED(x) \
  155. {#x, x ## _width, x ## _height, x ## _bits, x ## _mask_bits}
  156. static cursormap cursors[] = {
  157. CURSOR_ADD(arch), CURSOR_ADD( bsd), CURSOR_ADD(centos),
  158. CURSOR_ADD(debian), CURSOR_ADD(gentoo), CURSOR_ADD(mandriva),
  159. CURSOR_ADD(openbsd), CURSOR_ADD(slackware), CURSOR_ADD(suse),
  160. CURSOR_ADD(ubuntu), CURSOR_ADD(bucket),
  161. CURSOR_ADD_MASKED(hand_up), CURSOR_ADD_MASKED(hand_down),
  162. {NULL, 0, 0, NULL, NULL},
  163. };
  164. static void
  165. gtk_ui_set_cursor(MCursor *cursor) {
  166. gdk_window_set_cursor(field->window, cursor->cursor);
  167. }
  168. static void
  169. gtk_ui_load_cursor(const char *name, int masked, MCursor **cursorp) {
  170. MCursor *cursor;
  171. GdkBitmap *bitmap, *mask;
  172. cursormap *c;
  173. cursor = xalloc(sizeof *cursor);
  174. for (c = cursors; c->name != NULL; c++)
  175. if (strcmp(name, c->name) == 0)
  176. break;
  177. if (c->name == NULL)
  178. fatal("couldn't load cursor: %s", name);
  179. bitmap = gdk_bitmap_create_from_data(field->window, c->data,
  180. c->width, c->height);
  181. if (masked == CURSOR_SEP_MASK)
  182. mask = gdk_bitmap_create_from_data(field->window, c->maskdata,
  183. c->width, c->height);
  184. else
  185. mask = bitmap;
  186. cursor->cursor = gdk_cursor_new_from_pixmap(bitmap, mask,
  187. &black, &white,
  188. c->width/2, c->height/2);
  189. *cursorp = cursor;
  190. }
  191. /*
  192. * Pixmap handling
  193. */
  194. static void
  195. gtk_ui_load_picture(const char *name, int trans, Picture **pictp) {
  196. Picture *pict;
  197. char file[255];
  198. GdkBitmap *mask;
  199. UNUSED(trans);
  200. pict = xalloc(sizeof *pict);
  201. sprintf(file, "%s/pixmaps/%s.xpm", pictdir, name);
  202. pict->pix = gdk_pixmap_create_from_xpm(toplevel->window, &mask,
  203. NULL, file);
  204. if (pict->pix == NULL)
  205. fatal("error reading %s", file);
  206. pict->mask = mask;
  207. pict->gc = gdk_gc_new(toplevel->window);
  208. gdk_gc_set_exposures(pict->gc, FALSE);
  209. gdk_gc_set_clip_mask(pict->gc, mask);
  210. gdk_window_get_size(pict->pix, &pict->width, &pict->height);
  211. *pictp = pict;
  212. }
  213. static void
  214. gtk_ui_set_icon(Picture *icon) {
  215. gdk_window_set_icon(toplevel->window, NULL, icon->pix, icon->mask);
  216. }
  217. static int
  218. gtk_ui_picture_width(Picture *pict) {
  219. return (pict->width);
  220. }
  221. static int
  222. gtk_ui_picture_height(Picture *pict) {
  223. return (pict->height);
  224. }
  225. /*
  226. * Graphics operations
  227. */
  228. static void
  229. gtk_ui_clear_window(void) {
  230. gdk_draw_rectangle(offscreen, field->style->white_gc, TRUE, 0, 0,
  231. screensize, screensize);
  232. }
  233. static void
  234. gtk_ui_refresh_window(void) {
  235. gdk_draw_pixmap(field->window, stdgc, offscreen, 0, 0, 0, 0,
  236. screensize, screensize);
  237. }
  238. static void
  239. gtk_ui_draw_image(Picture *pict, int x, int y) {
  240. gdk_gc_set_clip_origin(pict->gc, x, y);
  241. gdk_draw_pixmap(offscreen, pict->gc, pict->pix, 0, 0, x, y,
  242. pict->width, pict->height);
  243. }
  244. static void
  245. gtk_ui_draw_line(int x1, int y1, int x2, int y2) {
  246. gdk_draw_line(offscreen, stdgc, x1, y1, x2, y2);
  247. }
  248. static void
  249. gtk_ui_draw_string(const char *str, int x, int y) {
  250. gdk_draw_string(offscreen, font, stdgc, x, y, str);
  251. }
  252. /*
  253. * Timer operations
  254. */
  255. static void
  256. gtk_ui_start_timer(int ms) {
  257. if (timer == 0)
  258. timer = gtk_timeout_add(ms, timer_tick, NULL);
  259. }
  260. static void
  261. gtk_ui_stop_timer(void) {
  262. if (timer != 0)
  263. gtk_timeout_remove(timer);
  264. timer = 0;
  265. }
  266. static int
  267. gtk_ui_timer_active(void) {
  268. return (!!timer);
  269. }
  270. /*
  271. * Main Loop
  272. */
  273. static void
  274. gtk_ui_main_loop(void) {
  275. gtk_main();
  276. }
  277. /*
  278. * Initialization
  279. */
  280. static void
  281. gtk_ui_initialize(int *argc, char **argv) {
  282. struct stat stats;
  283. gtk_init(argc, &argv);
  284. toplevel = gtk_window_new(GTK_WINDOW_TOPLEVEL);
  285. timer = 0;
  286. gtk_window_set_title(GTK_WINDOW(toplevel), "XLennart - An XBill Modification");
  287. gtk_signal_connect(GTK_OBJECT(toplevel), "delete_event",
  288. GTK_SIGNAL_FUNC(quit_game), NULL);
  289. if (stat(IMAGES, &stats) == 0)
  290. pictdir = IMAGES;
  291. else
  292. pictdir = ".";
  293. }
  294. static GtkWidget *
  295. new_menu_item(GtkWidget *menu, int dialog) {
  296. GtkWidget *menu_item;
  297. menu_item = gtk_menu_item_new_with_label(UI_menu_string(dialog));
  298. gtk_menu_append(GTK_MENU(menu), menu_item);
  299. gtk_signal_connect_object(GTK_OBJECT(menu_item), "activate",
  300. gtk_ui_popup_dialog, (gpointer) dialog);
  301. return (menu_item);
  302. }
  303. static GtkWidget *
  304. CreateMenuBar(void) {
  305. GtkWidget *menubar;
  306. GtkWidget *game_item, *game_menu;
  307. GtkWidget *info_item, *info_menu;
  308. GtkWidget *tearoff;
  309. menubar = gtk_menu_bar_new();
  310. game_item = gtk_menu_item_new_with_label("Game");
  311. game_menu = gtk_menu_new();
  312. tearoff = gtk_tearoff_menu_item_new();
  313. gtk_menu_append(GTK_MENU(game_menu), tearoff);
  314. new_menu_item(game_menu, DIALOG_NEWGAME);
  315. pausebutton = new_menu_item(game_menu, DIALOG_PAUSEGAME);
  316. new_menu_item(game_menu, DIALOG_WARPLEVEL);
  317. new_menu_item(game_menu, DIALOG_HIGHSCORE);
  318. new_menu_item(game_menu, DIALOG_QUITGAME);
  319. gtk_menu_bar_append(GTK_MENU_BAR(menubar), game_item);
  320. gtk_menu_item_set_submenu(GTK_MENU_ITEM(game_item), game_menu);
  321. info_item = gtk_menu_item_new_with_label("Info");
  322. info_menu = gtk_menu_new();
  323. tearoff = gtk_tearoff_menu_item_new();
  324. gtk_menu_append(GTK_MENU(info_menu), tearoff);
  325. new_menu_item(info_menu, DIALOG_STORY);
  326. new_menu_item(info_menu, DIALOG_RULES);
  327. new_menu_item(info_menu, DIALOG_ABOUT);
  328. gtk_menu_bar_append(GTK_MENU_BAR(menubar), info_item);
  329. gtk_menu_item_set_submenu(GTK_MENU_ITEM(info_item), info_menu);
  330. return menubar;
  331. }
  332. static GtkWidget *
  333. CreateDrawingArea(int width, int height) {
  334. GtkWidget *w = gtk_drawing_area_new();
  335. gtk_drawing_area_size(GTK_DRAWING_AREA(w), width, height);
  336. return w;
  337. }
  338. static void
  339. gtk_ui_make_main_window(int size) {
  340. GdkWindowHints flags;
  341. GdkGeometry geom;
  342. gint winwidth, winheight;
  343. screensize = size;
  344. base = gtk_vbox_new(FALSE, 0);
  345. gtk_container_add(GTK_CONTAINER(toplevel), base);
  346. menubar = CreateMenuBar();
  347. gtk_box_pack_start(GTK_BOX(base), menubar, FALSE, FALSE, 0);
  348. field = CreateDrawingArea(size, size);
  349. gtk_box_pack_start(GTK_BOX(base), field, FALSE, FALSE, 0);
  350. gtk_signal_connect(GTK_OBJECT(field), "button-press-event",
  351. GTK_SIGNAL_FUNC(button_press), NULL);
  352. gtk_signal_connect(GTK_OBJECT(field), "button-release-event",
  353. GTK_SIGNAL_FUNC(button_release), NULL);
  354. gtk_signal_connect(GTK_OBJECT(field), "enter-notify-event",
  355. GTK_SIGNAL_FUNC(enter_window), NULL);
  356. gtk_signal_connect(GTK_OBJECT(field), "leave-notify-event",
  357. GTK_SIGNAL_FUNC(leave_window), NULL);
  358. gtk_signal_connect(GTK_OBJECT(field), "expose-event",
  359. GTK_SIGNAL_FUNC(redraw_window), NULL);
  360. gtk_widget_set_events(field, GDK_BUTTON_PRESS_MASK |
  361. GDK_BUTTON_RELEASE_MASK |
  362. GDK_ENTER_NOTIFY_MASK |
  363. GDK_LEAVE_NOTIFY_MASK |
  364. GDK_EXPOSURE_MASK);
  365. gtk_widget_show_all(toplevel);
  366. gdk_window_get_size(toplevel->window, &winwidth, &winheight);
  367. geom.min_width = geom.max_width = geom.base_width = winwidth;
  368. geom.min_height = geom.max_height = geom.base_height = winheight;
  369. geom.width_inc = geom.height_inc = 0;
  370. flags = GDK_HINT_MIN_SIZE | GDK_HINT_MAX_SIZE | GDK_HINT_BASE_SIZE |
  371. GDK_HINT_RESIZE_INC;
  372. gdk_window_set_geometry_hints(toplevel->window, &geom, flags);
  373. gdk_color_parse("white", &white);
  374. gdk_color_parse("black", &black);
  375. }
  376. static void
  377. gtk_ui_graphics_init(void) {
  378. offscreen = gdk_pixmap_new(field->window, screensize, screensize, -1);
  379. stdgc = gdk_gc_new(offscreen);
  380. gdk_gc_set_exposures(stdgc, FALSE);
  381. gdk_gc_set_line_attributes(stdgc, 2, GDK_LINE_SOLID, GDK_CAP_ROUND,
  382. GDK_JOIN_MITER);
  383. font = gdk_font_load("fixed");
  384. }
  385. static GtkWidget *
  386. new_button(GtkWidget *dialog, const char *text, GtkSignalFunc func,
  387. GtkObject *obj)
  388. {
  389. GtkWidget *button = gtk_button_new_with_label(text);
  390. if (func != NULL)
  391. gtk_signal_connect_object(GTK_OBJECT(button), "clicked",
  392. func, obj);
  393. gtk_signal_connect_object(GTK_OBJECT(button), "clicked",
  394. GTK_SIGNAL_FUNC(gtk_widget_hide),
  395. GTK_OBJECT(dialog));
  396. gtk_signal_connect_object(GTK_OBJECT(button), "clicked",
  397. GTK_SIGNAL_FUNC(popdown), NULL);
  398. gtk_container_add(GTK_CONTAINER(GTK_DIALOG(dialog)->action_area),
  399. button);
  400. gtk_widget_show(button);
  401. return button;
  402. }
  403. static void
  404. CreateDialog(int index, int hascancel, Picture *icon,
  405. const char *buttonlabel, GtkSignalFunc func)
  406. {
  407. GtkWidget *dialog, *pixmap, *label, *hbox;
  408. dialog = gtk_dialog_new();
  409. gtk_window_set_modal(GTK_WINDOW(dialog), TRUE);
  410. hbox = gtk_hbox_new(FALSE, 0);
  411. if (icon != NULL) {
  412. pixmap = gtk_pixmap_new(icon->pix, icon->mask);
  413. gtk_container_add(GTK_CONTAINER(hbox), pixmap);
  414. }
  415. label = gtk_label_new(UI_dialog_string(index));
  416. gtk_container_add(GTK_CONTAINER(hbox), label);
  417. gtk_container_add(GTK_CONTAINER(GTK_DIALOG(dialog)->vbox), hbox);
  418. gtk_widget_show_all(hbox);
  419. if (buttonlabel == NULL)
  420. buttonlabel = "OK";
  421. new_button(dialog, buttonlabel, func, NULL);
  422. if (hascancel)
  423. new_button(dialog, "Cancel", NULL, NULL);
  424. gtk_widget_realize(dialog);
  425. dialogs[index] = dialog;
  426. }
  427. static void
  428. CreateEnterText(int index, GtkSignalFunc func) {
  429. GtkWidget *dialog, *label, *entry;
  430. dialog = gtk_dialog_new();
  431. gtk_window_set_modal(GTK_WINDOW(dialog), TRUE);
  432. label = gtk_label_new(UI_dialog_string(index));
  433. gtk_container_add(GTK_CONTAINER(GTK_DIALOG(dialog)->vbox), label);
  434. gtk_widget_show(label);
  435. entry = gtk_entry_new_with_max_length(20);
  436. gtk_signal_connect_object(GTK_OBJECT(entry), "activate",
  437. func, GTK_OBJECT(entry));
  438. gtk_signal_connect_object(GTK_OBJECT(entry), "activate",
  439. GTK_SIGNAL_FUNC(gtk_widget_hide),
  440. GTK_OBJECT(dialog));
  441. gtk_container_add(GTK_CONTAINER(GTK_DIALOG(dialog)->vbox), entry);
  442. gtk_widget_show(entry);
  443. new_button(dialog, "OK", func, GTK_OBJECT(entry));
  444. gtk_widget_realize(dialog);
  445. dialogs[index] = dialog;
  446. }
  447. static void
  448. CreatePixmapBox(int index, Picture *logo, Picture *pix) {
  449. GtkWidget *dialog, *pixmap, *label;
  450. const char *text = UI_dialog_string(index);
  451. dialog = gtk_dialog_new();
  452. gtk_window_set_modal(GTK_WINDOW(dialog), TRUE);
  453. pixmap = gtk_pixmap_new(logo->pix, logo->mask);
  454. gtk_container_add(GTK_CONTAINER(GTK_DIALOG(dialog)->vbox), pixmap);
  455. gtk_widget_show(pixmap);
  456. if (pix != NULL) {
  457. pixmap = gtk_pixmap_new(pix->pix, pix->mask);
  458. gtk_container_add(GTK_CONTAINER(GTK_DIALOG(dialog)->vbox),
  459. pixmap);
  460. gtk_widget_show(pixmap);
  461. }
  462. if (text != NULL) {
  463. label = gtk_label_new(text);
  464. gtk_container_add(GTK_CONTAINER(GTK_DIALOG(dialog)->vbox),
  465. label);
  466. gtk_widget_show(label);
  467. }
  468. new_button(dialog, "OK", NULL, NULL);
  469. gtk_widget_realize(dialog);
  470. dialogs[index] = dialog;
  471. }
  472. static void
  473. gtk_ui_create_dialogs(Picture *logo, Picture *icon, Picture *about) {
  474. CreateDialog(DIALOG_NEWGAME, 1, NULL, NULL, new_game);
  475. CreateDialog(DIALOG_PAUSEGAME, 0, icon, "Continue", NULL);
  476. CreateEnterText(DIALOG_WARPLEVEL, warp_apply);
  477. CreateDialog(DIALOG_HIGHSCORE, 0, NULL, NULL, NULL);
  478. CreateDialog(DIALOG_QUITGAME, 1, NULL, NULL, quit_game);
  479. CreatePixmapBox(DIALOG_STORY, logo, NULL);
  480. CreatePixmapBox(DIALOG_RULES, logo, NULL);
  481. CreatePixmapBox(DIALOG_ABOUT, logo, about);
  482. CreateDialog(DIALOG_SCORE, 0, NULL, NULL, NULL);
  483. CreateDialog(DIALOG_ENDGAME, 0, NULL, "Nuts!", NULL);
  484. CreateEnterText(DIALOG_ENTERNAME, enter_name);
  485. }
  486. static void
  487. set_label(GtkWidget *dialog, const char *str) {
  488. GList *list;
  489. GtkWidget *hbox = NULL;
  490. list = gtk_container_children(GTK_CONTAINER(GTK_DIALOG(dialog)->vbox));
  491. while (list != NULL) {
  492. GtkWidget *w = (GtkWidget *) list->data;
  493. list = g_list_next(list);
  494. if (GTK_IS_HBOX(w)) {
  495. hbox = w;
  496. break;
  497. }
  498. }
  499. if (hbox == NULL)
  500. return;
  501. list = gtk_container_children(GTK_CONTAINER(hbox));
  502. while (list != NULL) {
  503. GtkWidget *w = (GtkWidget *) list->data;
  504. list = g_list_next(list);
  505. if (GTK_IS_LABEL(w)) {
  506. gtk_label_set_text(GTK_LABEL(w), str);
  507. return;
  508. }
  509. }
  510. }
  511. static void
  512. gtk_ui_update_dialog(int index, const char *str) {
  513. set_label(dialogs[index], str);
  514. }
  515. static void
  516. gtk_ui_set_pausebutton(int active) {
  517. if (pausebutton != NULL)
  518. gtk_widget_set_sensitive(pausebutton, active);
  519. }
  520. static struct UI_methods gtk_methods = {
  521. gtk_ui_set_cursor,
  522. gtk_ui_load_cursor,
  523. gtk_ui_load_picture,
  524. gtk_ui_set_icon,
  525. gtk_ui_picture_width,
  526. gtk_ui_picture_height,
  527. gtk_ui_graphics_init,
  528. gtk_ui_clear_window,
  529. gtk_ui_refresh_window,
  530. gtk_ui_draw_image,
  531. gtk_ui_draw_line,
  532. gtk_ui_draw_string,
  533. gtk_ui_start_timer,
  534. gtk_ui_stop_timer,
  535. gtk_ui_timer_active,
  536. gtk_ui_popup_dialog,
  537. gtk_ui_main_loop,
  538. gtk_ui_initialize,
  539. gtk_ui_make_main_window,
  540. gtk_ui_create_dialogs,
  541. gtk_ui_set_pausebutton,
  542. gtk_ui_update_dialog,
  543. };
  544. void
  545. gtk_ui_setmethods(UI_methods **methodsp) {
  546. *methodsp = &gtk_methods;
  547. }