1 /*
2 * TilEm II
3 *
4 * Copyright (c) 2010-2011 Thibault Duponchelle
5 * Copyright (c) 2011 Benjamin Moody
6 *
7 * This program is free software: you can redistribute it and/or
8 * modify it under the terms of the GNU General Public License as
9 * published by the Free Software Foundation, either version 3 of the
10 * License, or (at your option) any later version.
11 *
12 * This program is distributed in the hope that it will be useful, but
13 * WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
15 * General Public License for more details.
16 *
17 * You should have received a copy of the GNU General Public License
18 * along with this program. If not, see <http://www.gnu.org/licenses/>.
19 */
20
21 #ifdef HAVE_CONFIG_H
22 # include <config.h>
23 #endif
24
25 #include <stdio.h>
26 #include <stdlib.h>
27 #include <string.h>
28 #include <gtk/gtk.h>
29 #include <glib/gstdio.h>
30 #include <ticalcs.h>
31 #include <tilem.h>
32
33 #include "gui.h"
34 #include "files.h"
35 #include "filedlg.h"
36 #include "msgbox.h"
37
38 #define DEFAULT_WIDTH_96 192
39 #define DEFAULT_HEIGHT_96 128
40 #define DEFAULT_WIDTH_128 256
41 #define DEFAULT_HEIGHT_128 128
42 #define DEFAULT_FORMAT "png"
43
44 struct imgsize {
45 int width;
46 int height;
47 };
48
49 static const struct imgsize normal_sizes[] =
50 { { 96, 64 }, { 192, 128 }, { 288, 192 } };
51
52 static const struct imgsize wide_sizes[] =
53 /* actual aspect ratio is 92:55 or 1.673:1 */
54 { { 128, 64 }, { 128, 77 },
55 { 214, 128 }, { 256, 128 }, { 256, 153 },
56 { 321, 192 }, { 384, 192 } };
57
58 static void grab_screen(GtkButton *btn, TilemScreenshotDialog *ssdlg);
59 static void begin_animation(GtkButton *btn, TilemScreenshotDialog *ssdlg);
60 static void end_animation(GtkButton *btn, TilemScreenshotDialog *ssdlg);
61 static gboolean save_output(TilemScreenshotDialog *ssdlg);
62
63 static char* find_free_filename(const char* directory,
64 const char* filename,
65 const char* extension);
66
67 /* Test if the calc has a wide screen (ti86) */
is_wide_screen(TilemCalcEmulator * emu)68 static gboolean is_wide_screen(TilemCalcEmulator *emu)
69 {
70 g_return_val_if_fail(emu != NULL, FALSE);
71 g_return_val_if_fail(emu->calc != NULL, FALSE);
72
73 return (emu->calc->hw.lcdwidth == 128);
74 }
75
76 /* Quick screenshot: save a screenshot with predefined settings,
77 without prompting the user */
quick_screenshot(TilemEmulatorWindow * ewin)78 void quick_screenshot(TilemEmulatorWindow *ewin)
79 {
80 char *folder, *filename, *format;
81 int grayscale, w96, h96, w128, h128, width, height;
82 TilemAnimation *anim;
83 GError *err = NULL;
84 GdkColor fg, bg;
85
86 tilem_config_get("screenshot",
87 "directory/f", &folder,
88 "format/s", &format,
89 "grayscale/b=1", &grayscale,
90 "width_96x64/i", &w96,
91 "height_96x64/i", &h96,
92 "width_128x64/i", &w128,
93 "height_128x64/i", &h128,
94 "foreground/c=#000", &fg,
95 "background/c=#fff", &bg,
96 NULL);
97
98 anim = tilem_calc_emulator_get_screenshot(ewin->emu, grayscale);
99 if (!anim) {
100 g_free(folder);
101 g_free(format);
102 return;
103 }
104
105 if (is_wide_screen(ewin->emu)) {
106 width = (w128 > 0 ? w128 : DEFAULT_WIDTH_128);
107 height = (h128 > 0 ? h128 : DEFAULT_HEIGHT_128);
108 }
109 else {
110 width = (w96 > 0 ? w96 : DEFAULT_WIDTH_96);
111 height = (h96 > 0 ? h96 : DEFAULT_HEIGHT_96);
112 }
113
114 tilem_animation_set_size(anim, width, height);
115 tilem_animation_set_colors(anim, &fg, &bg);
116
117 if (!folder)
118 folder = get_config_file_path("screenshots", NULL);
119
120 if (!format)
121 format = g_strdup(DEFAULT_FORMAT);
122
123 g_mkdir_with_parents(folder, 0755);
124
125 filename = find_free_filename(folder, "screenshot", format);
126 if (!filename) {
127 g_free(folder);
128 g_free(format);
129 g_object_unref(anim);
130 return;
131 }
132
133 printf("screenshot saved : %s\n", filename);
134
135 if (!tilem_animation_save(anim, filename, format, NULL, NULL, &err)) {
136 messagebox01(ewin->window, GTK_MESSAGE_ERROR,
137 "Unable to save screenshot",
138 "%s", err->message);
139 g_error_free(err);
140 }
141
142 g_object_unref(anim);
143 g_free(filename);
144 g_free(folder);
145 g_free(format);
146 }
147
148 /* Look for a free filename by testing [folder]/[basename]000.[extension] to [folder]/[basename]999.[extension]
149 Return a newly allocated string if success
150 Return null if no filename found */
find_free_filename(const char * folder,const char * basename,const char * extension)151 static char* find_free_filename(const char* folder,
152 const char* basename,
153 const char* extension)
154 {
155 int i;
156 char *filename, *prefix;
157
158 if(folder)
159 prefix = g_build_filename(folder, basename, NULL);
160 else
161 prefix = g_build_filename(basename, NULL);
162
163 /* I do not use a while and limit number to 1000 because for any reason, if there's a problem in this scope
164 I don't want to freeze tilem (if tilem don't find a free filename and never return anything)
165 Limit to 1000 prevent this problem but if you prefer we could use a while wich wait a valid filename... */
166 for(i=0; i<999; i++) {
167 filename = g_strdup_printf("%s%03d.%s", prefix, i, extension);
168 if(!g_file_test(filename, G_FILE_TEST_IS_REGULAR)) {
169 g_free(prefix);
170 return filename;
171 }
172 g_free(filename);
173 }
174
175 g_free(prefix);
176 return NULL;
177 }
178
179 /* Change the review image to set the current animation */
set_current_animation(TilemScreenshotDialog * ssdlg,TilemAnimation * anim)180 static void set_current_animation(TilemScreenshotDialog *ssdlg,
181 TilemAnimation *anim)
182 {
183 GtkImage *img = GTK_IMAGE(ssdlg->screenshot_preview_image);
184 int width, height;
185 GdkColor fg, bg;
186 gdouble speed;
187
188 if (anim)
189 g_object_ref(anim);
190 if (ssdlg->current_anim)
191 g_object_unref(ssdlg->current_anim);
192 ssdlg->current_anim = anim;
193
194 if (!anim) {
195 gtk_image_set_from_animation(img, NULL);
196 gtk_dialog_set_response_sensitive(GTK_DIALOG(ssdlg->window),
197 GTK_RESPONSE_ACCEPT, FALSE);
198 }
199 else {
200 width = gtk_spin_button_get_value_as_int
201 (GTK_SPIN_BUTTON(ssdlg->width_spin));
202 height = gtk_spin_button_get_value_as_int
203 (GTK_SPIN_BUTTON(ssdlg->height_spin));
204 tilem_animation_set_size(anim, width, height);
205
206 gtk_color_button_get_color
207 (GTK_COLOR_BUTTON(ssdlg->foreground_color), &fg);
208 gtk_color_button_get_color
209 (GTK_COLOR_BUTTON(ssdlg->background_color), &bg);
210 tilem_animation_set_colors(anim, &fg, &bg);
211
212 speed = gtk_spin_button_get_value
213 (GTK_SPIN_BUTTON(ssdlg->animation_speed));
214 tilem_animation_set_speed(anim, speed);
215
216 gtk_image_set_from_animation(img, GDK_PIXBUF_ANIMATION(anim));
217
218 /* Need to call gtk_widget_show because we hide it
219 while recording */
220 gtk_widget_show(ssdlg->screenshot_preview_image);
221
222 gtk_dialog_set_response_sensitive(GTK_DIALOG(ssdlg->window),
223 GTK_RESPONSE_ACCEPT, TRUE);
224 }
225 }
226
dialog_response(G_GNUC_UNUSED GtkDialog * dialog,gint response,gpointer data)227 static void dialog_response(G_GNUC_UNUSED GtkDialog *dialog, gint response, gpointer data)
228 {
229 TilemScreenshotDialog *ssdlg = data;
230
231 if (response == GTK_RESPONSE_ACCEPT) {
232 if (!save_output(ssdlg))
233 return;
234 }
235
236 gtk_widget_hide(GTK_WIDGET(dialog));
237 end_animation(NULL, ssdlg);
238 set_current_animation(ssdlg, NULL);
239 }
240
set_size_spin_buttons(TilemScreenshotDialog * ssdlg,int width,int height)241 static void set_size_spin_buttons(TilemScreenshotDialog *ssdlg,
242 int width, int height)
243 {
244 gtk_spin_button_set_value(GTK_SPIN_BUTTON(ssdlg->width_spin), width);
245 gtk_spin_button_set_value(GTK_SPIN_BUTTON(ssdlg->height_spin), height);
246 }
247
248 enum {
249 COL_TEXT,
250 COL_WIDTH,
251 COL_HEIGHT
252 };
253
animation_speed_changed(GtkSpinButton * animation_speed,gpointer data)254 static void animation_speed_changed(GtkSpinButton *animation_speed,
255 gpointer data)
256 {
257 TilemScreenshotDialog *ssdlg = data;
258 TilemAnimation * anim = ssdlg->current_anim;
259 gdouble value = gtk_spin_button_get_value(animation_speed);
260 tilem_animation_set_speed(anim, value);
261 }
262
263 /* Combo box changed. Update spin buttons accordingly. */
size_combo_changed(GtkComboBox * combo,TilemScreenshotDialog * ssdlg)264 static void size_combo_changed(GtkComboBox *combo,
265 TilemScreenshotDialog *ssdlg)
266 {
267 GtkTreeModel *model;
268 GtkTreeIter iter;
269 int width, height;
270
271 if (gtk_combo_box_get_active_iter(combo, &iter)) {
272 model = gtk_combo_box_get_model(combo);
273 gtk_tree_model_get(model, &iter,
274 COL_WIDTH, &width,
275 COL_HEIGHT, &height,
276 -1);
277 if (width && height)
278 set_size_spin_buttons(ssdlg, width, height);
279 }
280 }
281
size_spin_changed(G_GNUC_UNUSED GtkSpinButton * sb,TilemScreenshotDialog * ssdlg)282 static void size_spin_changed(G_GNUC_UNUSED GtkSpinButton *sb,
283 TilemScreenshotDialog *ssdlg)
284 {
285 GtkComboBox *combo = GTK_COMBO_BOX(ssdlg->ss_size_combo);
286 GtkTreeModel *model;
287 GtkTreeIter iter;
288 int width, height, w, h;
289
290 model = gtk_combo_box_get_model(combo);
291 if (!model || !gtk_tree_model_get_iter_first(model, &iter))
292 return;
293
294 width = gtk_spin_button_get_value_as_int
295 (GTK_SPIN_BUTTON(ssdlg->width_spin));
296 height = gtk_spin_button_get_value_as_int
297 (GTK_SPIN_BUTTON(ssdlg->height_spin));
298
299 do {
300 gtk_tree_model_get(model, &iter,
301 COL_WIDTH, &w,
302 COL_HEIGHT, &h,
303 -1);
304
305 if ((w == 0 && h == 0) || (w == width && h == height)) {
306 gtk_combo_box_set_active_iter(combo, &iter);
307 break;
308 }
309 } while (gtk_tree_model_iter_next(model, &iter));
310
311 set_current_animation(ssdlg, ssdlg->current_anim);
312 }
313
fill_size_combobox(GtkComboBox * combo,const struct imgsize * sizes,int nsizes)314 static void fill_size_combobox(GtkComboBox *combo,
315 const struct imgsize *sizes,
316 int nsizes)
317 {
318 GtkListStore *store;
319 GtkTreeIter iter;
320 int i;
321 char *s;
322
323 store = gtk_list_store_new(3, G_TYPE_STRING, G_TYPE_INT, G_TYPE_INT);
324
325 for (i = 0; i < nsizes; i++) {
326 s = g_strdup_printf("%d \303\227 %d",
327 sizes[i].width,
328 sizes[i].height);
329
330 gtk_list_store_append(store, &iter);
331 gtk_list_store_set(store, &iter,
332 COL_TEXT, s,
333 COL_WIDTH, sizes[i].width,
334 COL_HEIGHT, sizes[i].height,
335 -1);
336 g_free(s);
337 }
338
339 gtk_list_store_append(store, &iter);
340 gtk_list_store_set(store, &iter,
341 COL_TEXT, "Custom",
342 COL_WIDTH, 0,
343 COL_HEIGHT, 0,
344 -1);
345
346 gtk_combo_box_set_model(GTK_COMBO_BOX(combo), GTK_TREE_MODEL(store));
347 }
348
349 /* This method is called when a color is set (foreground or background)
350 * It set a new palette based on new custom colors
351 * It refresh the screen to print new colors
352 */
color_changed(G_GNUC_UNUSED GtkSpinButton * sb,TilemScreenshotDialog * ssdlg)353 static void color_changed(G_GNUC_UNUSED GtkSpinButton *sb,
354 TilemScreenshotDialog *ssdlg)
355 {
356 set_current_animation(ssdlg, ssdlg->current_anim);
357 }
358
359 /* Create the screenshot menu */
create_screenshot_window(TilemCalcEmulator * emu)360 static TilemScreenshotDialog * create_screenshot_window(TilemCalcEmulator *emu)
361 {
362 TilemScreenshotDialog *ssdlg = g_slice_new0(TilemScreenshotDialog);
363 GtkWidget *main_table, *vbox, *frame, *config_expander,
364 *tbl, *lbl, *align;
365 GtkCellRenderer *cell;
366
367 ssdlg->emu = emu;
368
369 ssdlg->window = gtk_dialog_new_with_buttons
370 ("Screenshot",
371 (emu->ewin ? GTK_WINDOW(emu->ewin->window) : NULL),
372 GTK_DIALOG_DESTROY_WITH_PARENT,
373 GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL,
374 GTK_STOCK_SAVE, GTK_RESPONSE_ACCEPT,
375 NULL);
376
377 gtk_window_set_resizable(GTK_WINDOW(ssdlg->window), FALSE);
378
379 gtk_dialog_set_alternative_button_order(GTK_DIALOG(ssdlg->window),
380 GTK_RESPONSE_ACCEPT,
381 GTK_RESPONSE_CANCEL,
382 -1);
383
384 gtk_dialog_set_default_response(GTK_DIALOG(ssdlg->window),
385 GTK_RESPONSE_ACCEPT);
386
387 g_signal_connect(ssdlg->window, "response",
388 G_CALLBACK(dialog_response), ssdlg);
389
390 g_signal_connect(ssdlg->window, "delete-event",
391 G_CALLBACK(gtk_widget_hide_on_delete), NULL);
392
393 main_table = gtk_table_new(2, 2, FALSE);
394 gtk_table_set_row_spacings(GTK_TABLE(main_table), 6);
395 gtk_table_set_col_spacings(GTK_TABLE(main_table), 12);
396 gtk_container_set_border_width(GTK_CONTAINER(main_table), 6);
397
398 /* Preview */
399
400 frame = gtk_frame_new("Preview");
401 gtk_frame_set_shadow_type(GTK_FRAME(frame), GTK_SHADOW_NONE);
402
403 ssdlg->screenshot_preview_image = gtk_image_new();
404 align = gtk_alignment_new(0.0, 0.0, 0.0, 0.0);
405 gtk_alignment_set_padding(GTK_ALIGNMENT(align), 0, 0, 12, 0);
406 gtk_container_add(GTK_CONTAINER(align), ssdlg->screenshot_preview_image);
407
408 gtk_container_add(GTK_CONTAINER(frame), align);
409 gtk_table_attach(GTK_TABLE(main_table), frame, 0, 1, 0, 1,
410 GTK_EXPAND | GTK_FILL, GTK_EXPAND | GTK_FILL, 0, 0);
411
412 /* Buttons */
413
414 vbox = gtk_vbutton_box_new();
415 gtk_button_box_set_layout(GTK_BUTTON_BOX(vbox), GTK_BUTTONBOX_START);
416 gtk_box_set_spacing(GTK_BOX(vbox), 6);
417
418 ssdlg->screenshot = gtk_button_new_with_mnemonic("_Grab");
419 gtk_box_pack_start(GTK_BOX(vbox), ssdlg->screenshot, FALSE, FALSE, 0);
420
421 ssdlg->record = gtk_button_new_with_mnemonic("_Record");
422 gtk_box_pack_start(GTK_BOX(vbox), ssdlg->record, FALSE, FALSE, 0);
423
424 ssdlg->stop = gtk_button_new_with_mnemonic("_Stop");
425 gtk_box_pack_start(GTK_BOX(vbox), ssdlg->stop, FALSE, FALSE, 0);
426 gtk_widget_set_sensitive(GTK_WIDGET(ssdlg->stop), FALSE);
427
428 gtk_table_attach(GTK_TABLE(main_table), vbox, 1, 2, 0, 2,
429 GTK_FILL, GTK_EXPAND | GTK_FILL, 0, 0);
430
431 /* Options */
432
433 config_expander = gtk_expander_new("Options");
434
435 tbl = gtk_table_new(7, 2, FALSE);
436 gtk_table_set_row_spacings(GTK_TABLE(tbl), 6);
437 gtk_table_set_col_spacings(GTK_TABLE(tbl), 6);
438
439 ssdlg->grayscale_tb = gtk_check_button_new_with_mnemonic("Gra_yscale");
440 gtk_table_attach(GTK_TABLE(tbl), ssdlg->grayscale_tb,
441 0, 2, 0, 1, GTK_EXPAND | GTK_FILL, GTK_FILL, 0, 0);
442
443 lbl = gtk_label_new_with_mnemonic("Image si_ze:");
444 gtk_misc_set_alignment(GTK_MISC(lbl), LABEL_X_ALIGN, 0.5);
445 gtk_table_attach(GTK_TABLE(tbl), lbl,
446 0, 1, 1, 2, GTK_FILL, GTK_FILL, 0, 0);
447
448 ssdlg->ss_size_combo = gtk_combo_box_new();
449 cell = gtk_cell_renderer_text_new();
450 gtk_cell_layout_pack_start(GTK_CELL_LAYOUT(ssdlg->ss_size_combo),
451 cell, TRUE);
452 gtk_cell_layout_set_attributes(GTK_CELL_LAYOUT(ssdlg->ss_size_combo),
453 cell, "text", COL_TEXT, NULL);
454 gtk_label_set_mnemonic_widget(GTK_LABEL(lbl), ssdlg->ss_size_combo);
455 gtk_table_attach(GTK_TABLE(tbl), ssdlg->ss_size_combo,
456 1, 2, 1, 2, GTK_EXPAND | GTK_FILL, GTK_FILL, 0, 0);
457
458 lbl = gtk_label_new_with_mnemonic("_Width:");
459 gtk_misc_set_alignment(GTK_MISC(lbl), LABEL_X_ALIGN, 0.5);
460 gtk_table_attach(GTK_TABLE(tbl), lbl,
461 0, 1, 2, 3, GTK_FILL, GTK_FILL, 0, 0);
462
463 ssdlg->width_spin = gtk_spin_button_new_with_range(1, 750, 1);
464 gtk_label_set_mnemonic_widget(GTK_LABEL(lbl), ssdlg->width_spin);
465 align = gtk_alignment_new(0.0, 0.5, 0.0, 1.0);
466 gtk_container_add(GTK_CONTAINER(align), ssdlg->width_spin);
467 gtk_table_attach(GTK_TABLE(tbl), align,
468 1, 2, 2, 3, GTK_FILL, GTK_FILL, 0, 0);
469
470 lbl = gtk_label_new_with_mnemonic("_Height:");
471 gtk_misc_set_alignment(GTK_MISC(lbl), LABEL_X_ALIGN, 0.5);
472 gtk_table_attach(GTK_TABLE(tbl), lbl,
473 0, 1, 3, 4, GTK_FILL, GTK_FILL, 0, 0);
474
475 ssdlg->height_spin = gtk_spin_button_new_with_range(1, 500, 1);
476 gtk_label_set_mnemonic_widget(GTK_LABEL(lbl), ssdlg->height_spin);
477 align = gtk_alignment_new(0.0, 0.5, 0.0, 1.0);
478 gtk_container_add(GTK_CONTAINER(align), ssdlg->height_spin);
479 gtk_table_attach(GTK_TABLE(tbl), align,
480 1, 2, 3, 4, GTK_FILL, GTK_FILL, 0, 0);
481
482
483 lbl = gtk_label_new_with_mnemonic("Animation s_peed:");
484 gtk_misc_set_alignment(GTK_MISC(lbl), LABEL_X_ALIGN, 0.5);
485 gtk_table_attach(GTK_TABLE(tbl), lbl,
486 0, 1, 4, 5, GTK_FILL, GTK_FILL, 0, 0);
487
488 ssdlg->animation_speed = gtk_spin_button_new_with_range(0.1, 100.0, 0.1);
489 gtk_label_set_mnemonic_widget(GTK_LABEL(lbl), ssdlg->animation_speed);
490 gtk_spin_button_set_value(GTK_SPIN_BUTTON(ssdlg->animation_speed), 1.0);
491 align = gtk_alignment_new(0.0, 0.5, 0.0, 1.0);
492 gtk_container_add(GTK_CONTAINER(align), ssdlg->animation_speed);
493 gtk_table_attach(GTK_TABLE(tbl), align,
494 1, 2, 4, 5, GTK_FILL, GTK_FILL, 0, 0);
495
496 /* Foreground color and background color */
497 lbl = gtk_label_new_with_mnemonic("_Foreground:");
498 gtk_misc_set_alignment(GTK_MISC(lbl), LABEL_X_ALIGN, 0.5);
499 gtk_table_attach(GTK_TABLE(tbl), lbl,
500 0, 1, 5, 6, GTK_FILL, GTK_FILL, 0, 0);
501
502 ssdlg->foreground_color = gtk_color_button_new();
503 gtk_label_set_mnemonic_widget(GTK_LABEL(lbl), ssdlg->foreground_color);
504 align = gtk_alignment_new(0.0, 0.5, 0.0, 1.0);
505 gtk_container_add(GTK_CONTAINER(align), ssdlg->foreground_color);
506 gtk_table_attach(GTK_TABLE(tbl), align,
507 1, 2, 5, 6, GTK_FILL, GTK_FILL, 0, 0);
508
509 lbl = gtk_label_new_with_mnemonic("_Background:");
510 gtk_misc_set_alignment(GTK_MISC(lbl), LABEL_X_ALIGN, 0.5);
511 gtk_table_attach(GTK_TABLE(tbl), lbl,
512 0, 1, 6, 7, GTK_FILL, GTK_FILL, 0, 0);
513
514 ssdlg->background_color = gtk_color_button_new();
515 gtk_label_set_mnemonic_widget(GTK_LABEL(lbl), ssdlg->background_color);
516 align = gtk_alignment_new(0.0, 0.5, 0.0, 1.0);
517 gtk_container_add(GTK_CONTAINER(align), ssdlg->background_color);
518 gtk_table_attach(GTK_TABLE(tbl), align,
519 1, 2, 6, 7, GTK_FILL, GTK_FILL, 0, 0);
520
521 align = gtk_alignment_new(0.5, 0.5, 1.0, 1.0);
522 gtk_alignment_set_padding(GTK_ALIGNMENT(align), 0, 0, 12, 0);
523 gtk_container_add(GTK_CONTAINER(align), tbl);
524
525 gtk_container_add(GTK_CONTAINER(config_expander), align);
526
527 gtk_table_attach(GTK_TABLE(main_table), config_expander, 0, 1, 1, 2,
528 GTK_FILL, GTK_FILL, 0, 0);
529
530 g_signal_connect(ssdlg->screenshot, "clicked",
531 G_CALLBACK(grab_screen), ssdlg);
532 g_signal_connect(ssdlg->record, "clicked",
533 G_CALLBACK(begin_animation), ssdlg);
534 g_signal_connect(ssdlg->stop, "clicked",
535 G_CALLBACK(end_animation), ssdlg);
536
537 g_signal_connect(ssdlg->ss_size_combo, "changed",
538 G_CALLBACK(size_combo_changed), ssdlg);
539 g_signal_connect(ssdlg->width_spin, "value-changed",
540 G_CALLBACK(size_spin_changed), ssdlg);
541 g_signal_connect(ssdlg->height_spin, "value-changed",
542 G_CALLBACK(size_spin_changed), ssdlg);
543 g_signal_connect(ssdlg->animation_speed, "value-changed",
544 G_CALLBACK(animation_speed_changed), ssdlg);
545
546 g_signal_connect(ssdlg->foreground_color, "color-set",
547 G_CALLBACK(color_changed), ssdlg);
548 g_signal_connect(ssdlg->background_color, "color-set",
549 G_CALLBACK(color_changed), ssdlg);
550 /*g_signal_connect(config_expander, "activate",
551 G_CALLBACK(on_config_expander_activate), ssdlg);
552 */
553 vbox = gtk_dialog_get_content_area(GTK_DIALOG(ssdlg->window));
554 gtk_container_add(GTK_CONTAINER(vbox), main_table);
555 gtk_widget_show_all(main_table);
556
557 return ssdlg;
558 }
559
560 /* Popup the screenshot window */
popup_screenshot_window(TilemEmulatorWindow * ewin)561 void popup_screenshot_window(TilemEmulatorWindow *ewin)
562 {
563 TilemScreenshotDialog *ssdlg;
564 int w96, h96, w128, h128, width, height, grayscale;
565 GdkColor fg, bg;
566
567 g_return_if_fail(ewin != NULL);
568 g_return_if_fail(ewin->emu != NULL);
569
570 if (!ewin->emu->ssdlg)
571 ewin->emu->ssdlg = create_screenshot_window(ewin->emu);
572 ssdlg = ewin->emu->ssdlg;
573
574 tilem_config_get("screenshot",
575 "grayscale/b=1", &grayscale,
576 "width_96x64/i", &w96,
577 "height_96x64/i", &h96,
578 "width_128x64/i", &w128,
579 "height_128x64/i", &h128,
580 "foreground/c=#000", &fg,
581 "background/c=#fff", &bg,
582 NULL);
583
584 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(ssdlg->grayscale_tb),
585 grayscale);
586
587 if (is_wide_screen(ewin->emu)) {
588 fill_size_combobox(GTK_COMBO_BOX(ssdlg->ss_size_combo),
589 wide_sizes, G_N_ELEMENTS(wide_sizes));
590 width = (w128 > 0 ? w128 : DEFAULT_WIDTH_128);
591 height = (h128 > 0 ? h128 : DEFAULT_HEIGHT_128);
592 }
593 else {
594 fill_size_combobox(GTK_COMBO_BOX(ssdlg->ss_size_combo),
595 normal_sizes, G_N_ELEMENTS(normal_sizes));
596 width = (w96 > 0 ? w96 : DEFAULT_WIDTH_96);
597 height = (h96 > 0 ? h96 : DEFAULT_HEIGHT_96);
598 }
599
600 set_size_spin_buttons(ssdlg, width, height);
601 size_spin_changed(NULL, ssdlg);
602
603 gtk_color_button_set_color(GTK_COLOR_BUTTON(ssdlg->foreground_color), &fg);
604 gtk_color_button_set_color(GTK_COLOR_BUTTON(ssdlg->background_color), &bg);
605
606 grab_screen(NULL, ssdlg);
607 gtk_window_present(GTK_WINDOW(ssdlg->window));
608 }
609
610 /* Save the current (static) output */
save_output(TilemScreenshotDialog * ssdlg)611 static gboolean save_output(TilemScreenshotDialog *ssdlg)
612 {
613 char *dir, *format, *filename, *basename;
614 TilemAnimation *anim = ssdlg->current_anim;
615 GdkPixbufAnimation *ganim = GDK_PIXBUF_ANIMATION(anim);
616 const char *format_opt, *width_opt, *height_opt;
617 gboolean is_static;
618 int width, height;
619 GdkColor fg, bg;
620 GError *err = NULL;
621
622 g_return_val_if_fail(anim != NULL, FALSE);
623
624 is_static = gdk_pixbuf_animation_is_static_image(ganim);
625 width = gdk_pixbuf_animation_get_width(ganim);
626 height = gdk_pixbuf_animation_get_height(ganim);
627
628 gtk_color_button_get_color
629 (GTK_COLOR_BUTTON(ssdlg->foreground_color), &fg);
630 gtk_color_button_get_color
631 (GTK_COLOR_BUTTON(ssdlg->background_color), &bg);
632
633 tilem_config_get("screenshot",
634 "directory/f", &dir,
635 "static_format/s", &format,
636 NULL);
637
638 if (!dir)
639 dir = g_get_current_dir();
640
641 if (!is_static) {
642 g_free(format);
643 format = g_strdup("gif");
644 }
645 else if (!format) {
646 format = g_strdup(DEFAULT_FORMAT);
647 }
648
649 filename = find_free_filename(dir, "screenshot", format);
650 basename = (filename ? g_filename_display_basename(filename) : NULL);
651 g_free(filename);
652 g_free(format);
653
654 if (!is_static) {
655 filename = prompt_save_file("Save Screenshot",
656 GTK_WINDOW(ssdlg->window),
657 basename, dir,
658 "GIF images", "*.gif",
659 "All files", "*",
660 NULL);
661 }
662 else {
663 /* FIXME: perhaps check the list of supported output
664 formats (gdk_pixbuf_get_formats()) - e.g., tiff is
665 usually supported, although it requires libtiff
666 installed (png and jpeg also require external
667 libraries, but we need those libraries anyway for
668 other reasons) */
669 filename = prompt_save_file("Save Screenshot",
670 GTK_WINDOW(ssdlg->window),
671 basename, dir,
672 "PNG images", "*.png",
673 "GIF images", "*.gif",
674 "BMP images", "*.bmp",
675 "JPEG images", "*.jpg;*.jpe;*.jpeg",
676 "All files", "*",
677 NULL);
678 }
679
680 g_free(basename);
681 g_free(dir);
682
683 if (!filename)
684 return FALSE;
685
686 if (!is_static) {
687 format = g_strdup("gif");
688 }
689 else {
690 basename = g_path_get_basename(filename);
691 format = strrchr(basename, '.');
692 if (!format) {
693 messagebox00(ssdlg->window, GTK_MESSAGE_ERROR,
694 "Unable to save screenshot",
695 "File name does not have a"
696 " recognized suffix");
697 g_free(filename);
698 g_free(basename);
699 return FALSE;
700 }
701 else {
702 format = g_strdup(format + 1);
703 }
704 }
705
706 tilem_animation_save(anim, filename, format, NULL, NULL, &err);
707
708 dir = g_path_get_dirname(filename);
709
710 if (err) {
711 messagebox01(ssdlg->window, GTK_MESSAGE_ERROR,
712 "Unable to save screenshot",
713 "%s", err->message);
714 g_error_free(err);
715 g_free(dir);
716 g_free(filename);
717 g_free(format);
718 return FALSE;
719 }
720
721 if (is_static)
722 format_opt = "static_format/s";
723 else
724 format_opt = NULL;
725
726 if (is_wide_screen(ssdlg->emu)) {
727 width_opt = "width_128x64/i";
728 height_opt = "height_128x64/i";
729 }
730 else {
731 width_opt = "width_96x64/i";
732 height_opt = "height_96x64/i";
733 }
734
735 tilem_config_set("screenshot",
736 "directory/f", dir,
737 "grayscale/b", ssdlg->current_anim_grayscale,
738 "foreground/c", &fg,
739 "background/c", &bg,
740 width_opt, width,
741 height_opt, height,
742 format_opt, format,
743 NULL);
744
745 g_free(dir);
746 g_free(filename);
747 g_free(format);
748 return TRUE;
749 }
750
751 /* Callback for record button */
begin_animation(G_GNUC_UNUSED GtkButton * btn,TilemScreenshotDialog * ssdlg)752 static void begin_animation(G_GNUC_UNUSED GtkButton *btn,
753 TilemScreenshotDialog *ssdlg)
754 {
755 gboolean grayscale = gtk_toggle_button_get_active
756 (GTK_TOGGLE_BUTTON(ssdlg->grayscale_tb));
757
758 gtk_widget_set_sensitive(GTK_WIDGET(ssdlg->animation_speed), FALSE);
759 gtk_widget_set_sensitive(GTK_WIDGET(ssdlg->screenshot), FALSE);
760 gtk_widget_set_sensitive(GTK_WIDGET(ssdlg->record), FALSE);
761 gtk_widget_set_sensitive(GTK_WIDGET(ssdlg->stop), TRUE);
762 gtk_dialog_set_response_sensitive(GTK_DIALOG(ssdlg->window),
763 GTK_RESPONSE_ACCEPT, FALSE);
764
765 tilem_calc_emulator_begin_animation(ssdlg->emu, grayscale);
766 ssdlg->current_anim_grayscale = grayscale;
767
768 /* You can choose to hide current animation while recording or not
769 It's as you prefer... For the moment I hide it */
770 /*gtk_widget_hide(GTK_WIDGET(ssdlg->screenshot_preview_image)); */
771
772 //set_current_animation(ssdlg, NULL);
773 }
774
775 /* Callback for stop button (stop the recording) */
end_animation(G_GNUC_UNUSED GtkButton * btn,TilemScreenshotDialog * ssdlg)776 static void end_animation(G_GNUC_UNUSED GtkButton *btn,
777 TilemScreenshotDialog *ssdlg)
778 {
779 TilemAnimation *anim;
780
781 if (ssdlg->emu->anim) {
782 anim = tilem_calc_emulator_end_animation(ssdlg->emu);
783 set_current_animation(ssdlg, anim);
784 g_object_unref(anim);
785 }
786 else {
787 set_current_animation(ssdlg, NULL);
788 }
789
790 gtk_widget_set_sensitive(GTK_WIDGET(ssdlg->animation_speed), TRUE);
791 gtk_widget_set_sensitive(GTK_WIDGET(ssdlg->screenshot), TRUE);
792 gtk_widget_set_sensitive(GTK_WIDGET(ssdlg->record), TRUE);
793 gtk_widget_set_sensitive(GTK_WIDGET(ssdlg->stop), FALSE);
794 }
795
796 /* Callback for screenshot button (take a screenshot) */
grab_screen(G_GNUC_UNUSED GtkButton * btn,TilemScreenshotDialog * ssdlg)797 static void grab_screen(G_GNUC_UNUSED GtkButton *btn,
798 TilemScreenshotDialog *ssdlg)
799 {
800 TilemAnimation *anim;
801
802 gboolean grayscale = gtk_toggle_button_get_active
803 (GTK_TOGGLE_BUTTON(ssdlg->grayscale_tb));
804
805 anim = tilem_calc_emulator_get_screenshot(ssdlg->emu, grayscale);
806 ssdlg->current_anim_grayscale = grayscale;
807 set_current_animation(ssdlg, anim);
808 g_object_unref(anim);
809 }
810
811