/* * TilEm II * * Copyright (c) 2010-2011 Thibault Duponchelle * Copyright (c) 2010-2012 Benjamin Moody * * 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 3 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, see . */ #ifdef HAVE_CONFIG_H # include #endif #include #include #include #include #include #include "gui.h" #include "files.h" #include "msgbox.h" /* Set size hints for the toplevel window */ static void set_size_hints(GtkWidget *widget, gpointer data) { TilemEmulatorWindow *ewin = data; /* Don't use gtk_window_set_geometry_hints() (which would appear to do what we want) because, in addition to setting the hints we want, that function causes GTK+ to argue with the window manager. Instead, we call this function after the check-resize signal (which is when GTK+ itself would normally set the hints.) FIXME: check that this works as desired on Win32/Quartz. */ if (gtk_widget_get_window(widget)) gdk_window_set_geometry_hints(gtk_widget_get_window(widget), &ewin->geomhints, ewin->geomhintmask); } static void window_state_changed(G_GNUC_UNUSED GtkWidget *w, GdkEventWindowState *event, gpointer data) { TilemEmulatorWindow *ewin = data; ewin->window_state = event->new_window_state; } static gboolean window_maximized(TilemEmulatorWindow *ewin) { return (ewin->window_state & (GDK_WINDOW_STATE_MAXIMIZED | GDK_WINDOW_STATE_FULLSCREEN)); } static gboolean screen_repaint(GtkWidget *w, GdkEventExpose *ev G_GNUC_UNUSED, TilemEmulatorWindow *ewin) { GtkAllocation alloc; GdkWindow *win; GtkStyle *style; gtk_widget_get_allocation(w, &alloc); /* If image buffer is not the correct size, allocate a new one */ if (!ewin->lcd_image_buf || alloc.width != ewin->lcd_image_width || alloc.height != ewin->lcd_image_height) { ewin->lcd_image_width = alloc.width; ewin->lcd_image_height = alloc.height; g_free(ewin->lcd_image_buf); ewin->lcd_image_buf = g_new(byte, alloc.width * alloc.height); } /* Draw LCD contents into the image buffer */ g_mutex_lock(ewin->emu->lcd_mutex); ewin->emu->lcd_update_pending = FALSE; tilem_draw_lcd_image_indexed(ewin->emu->lcd_buffer, ewin->lcd_image_buf, alloc.width, alloc.height, alloc.width, (ewin->lcd_smooth_scale ? TILEM_SCALE_SMOOTH : TILEM_SCALE_FAST)); g_mutex_unlock(ewin->emu->lcd_mutex); /* Render buffer to the screen */ win = gtk_widget_get_window(w); style = gtk_widget_get_style(w); gdk_draw_indexed_image(win, style->fg_gc[GTK_STATE_NORMAL], 0, 0, alloc.width, alloc.height, GDK_RGB_DITHER_NONE, ewin->lcd_image_buf, alloc.width, ewin->lcd_cmap); return TRUE; } /* Set the color palette for drawing the emulated LCD. */ static void screen_restyle(GtkWidget* w, GtkStyle* oldstyle G_GNUC_UNUSED, TilemEmulatorWindow* ewin) { dword* palette; GtkStyle *style; int r_dark, g_dark, b_dark; int r_light, g_light, b_light; double gamma = 2.2; if (!ewin->skin) { /* no skin -> use standard GTK colors */ style = gtk_widget_get_style(w); r_dark = style->text[GTK_STATE_NORMAL].red / 257; g_dark = style->text[GTK_STATE_NORMAL].green / 257; b_dark = style->text[GTK_STATE_NORMAL].blue / 257; r_light = style->base[GTK_STATE_NORMAL].red / 257; g_light = style->base[GTK_STATE_NORMAL].green / 257; b_light = style->base[GTK_STATE_NORMAL].blue / 257; } else { /* use skin colors */ r_dark = ((ewin->skin->lcd_black >> 16) & 0xff); g_dark = ((ewin->skin->lcd_black >> 8) & 0xff); b_dark = (ewin->skin->lcd_black & 0xff); r_light = ((ewin->skin->lcd_white >> 16) & 0xff); g_light = ((ewin->skin->lcd_white >> 8) & 0xff); b_light = (ewin->skin->lcd_white & 0xff); } /* Generate a new palette, and convert it into GDK format */ if (ewin->lcd_cmap) gdk_rgb_cmap_free(ewin->lcd_cmap); palette = tilem_color_palette_new(r_light, g_light, b_light, r_dark, g_dark, b_dark, gamma); ewin->lcd_cmap = gdk_rgb_cmap_new(palette, 256); tilem_free(palette); gtk_widget_queue_draw(ewin->lcd); } static void skin_size_allocate(GtkWidget *widget, GtkAllocation *alloc, gpointer data) { TilemEmulatorWindow *ewin = data; int rawwidth, rawheight, scaledwidth, scaledheight; int lcdleft, lcdright, lcdtop, lcdbottom; double rx, ry, r; g_return_if_fail(ewin->skin != NULL); rawwidth = gdk_pixbuf_get_width(ewin->skin->raw); rawheight = gdk_pixbuf_get_height(ewin->skin->raw); rx = (double) alloc->width / rawwidth; ry = (double) alloc->height / rawheight; r = MIN(rx, ry); scaledwidth = rawwidth * r; scaledheight = rawheight * r; if (ewin->skin->width == scaledwidth && ewin->skin->height == scaledheight) return; ewin->skin->width = scaledwidth; ewin->skin->height = scaledheight; ewin->skin->sx = ewin->skin->sy = 1.0 / r; if (ewin->skin->image) g_object_unref(ewin->skin->image); ewin->skin->image = gdk_pixbuf_scale_simple(ewin->skin->raw, scaledwidth, scaledheight, GDK_INTERP_BILINEAR); gtk_image_set_from_pixbuf(GTK_IMAGE(ewin->background), ewin->skin->image); lcdleft = ewin->skin->lcd_pos.left * r + 0.5; lcdright = ewin->skin->lcd_pos.right * r + 0.5; lcdtop = ewin->skin->lcd_pos.top * r + 0.5; lcdbottom = ewin->skin->lcd_pos.bottom * r + 0.5; gtk_widget_set_size_request(ewin->lcd, MAX(lcdright - lcdleft, 1), MAX(lcdbottom - lcdtop, 1)); gtk_layout_move(GTK_LAYOUT(widget), ewin->lcd, lcdleft, lcdtop); ewin->zoom_factor = r / ewin->base_zoom; if (ewin->zoom_factor <= 1.0) ewin->zoom_factor = 1.0; } static void noskin_size_allocate(G_GNUC_UNUSED GtkWidget *widget, GtkAllocation *alloc, gpointer data) { TilemEmulatorWindow *ewin = data; int lcdwidth, lcdheight; g_return_if_fail(ewin->emu->calc != NULL); lcdwidth = ewin->emu->calc->hw.lcdwidth; lcdheight = ewin->emu->calc->hw.lcdheight; if (alloc->width > alloc->height) ewin->zoom_factor = (gdouble) alloc->width / lcdwidth; else ewin->zoom_factor = (gdouble) alloc->height / lcdheight; if (ewin->zoom_factor <= 1.0) ewin->zoom_factor = 1.0; } /* Used when you load another skin */ void redraw_screen(TilemEmulatorWindow *ewin) { GtkWidget *pImage; GtkWidget *emuwin; int lcdwidth, lcdheight; int screenwidth, screenheight; int minwidth, minheight, defwidth, defheight, curwidth, curheight; double sx, sy, s, a1, a2; GError *err = NULL; if (ewin->skin) { skin_unload(ewin->skin); g_free(ewin->skin); } if (ewin->skin_disabled || !ewin->skin_file_name) { ewin->skin = NULL; } else { ewin->skin = g_new0(SKIN_INFOS, 1); if (skin_load(ewin->skin, ewin->skin_file_name, &err)) { skin_unload(ewin->skin); ewin->skin = NULL; } } if (ewin->emu->calc) { lcdwidth = ewin->emu->calc->hw.lcdwidth; lcdheight = ewin->emu->calc->hw.lcdheight; } else { lcdwidth = lcdheight = 1; } if (ewin->lcd) gtk_widget_destroy(ewin->lcd); if (ewin->background) gtk_widget_destroy(ewin->background); if (ewin->layout) gtk_widget_destroy(ewin->layout); /* create LCD widget */ ewin->lcd = gtk_drawing_area_new(); gtk_widget_set_double_buffered(ewin->lcd, FALSE); /* create background image and layout */ if (ewin->skin) { ewin->layout = gtk_layout_new(NULL, NULL); pImage = gtk_image_new(); gtk_layout_put(GTK_LAYOUT(ewin->layout), pImage, 0, 0); ewin->background = pImage; screenwidth = (ewin->skin->lcd_pos.right - ewin->skin->lcd_pos.left); screenheight = (ewin->skin->lcd_pos.bottom - ewin->skin->lcd_pos.top); gtk_widget_set_size_request(ewin->lcd, screenwidth, screenheight); gtk_layout_put(GTK_LAYOUT(ewin->layout), ewin->lcd, ewin->skin->lcd_pos.left, ewin->skin->lcd_pos.top); g_signal_connect(ewin->layout, "size-allocate", G_CALLBACK(skin_size_allocate), ewin); emuwin = ewin->layout; defwidth = ewin->skin->width; defheight = ewin->skin->height; sx = (double) lcdwidth / screenwidth; sy = (double) lcdheight / screenheight; s = MAX(sx, sy); minwidth = defwidth * s + 0.5; minheight = defheight * s + 0.5; ewin->base_zoom = s; } else { ewin->layout = NULL; ewin->background = NULL; emuwin = ewin->lcd; g_signal_connect(ewin->lcd, "size-allocate", G_CALLBACK(noskin_size_allocate), ewin); defwidth = minwidth = lcdwidth; defheight = minheight = lcdheight; ewin->base_zoom = 1.0; } curwidth = defwidth * ewin->base_zoom * ewin->zoom_factor + 0.5; curheight = defheight * ewin->base_zoom * ewin->zoom_factor + 0.5; gtk_widget_set_can_focus(emuwin, TRUE); gtk_widget_add_events(emuwin, (GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK | GDK_BUTTON1_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_DROP_FINISHED | GDK_DRAG_MOTION)); g_signal_connect(ewin->lcd, "expose-event", G_CALLBACK(screen_repaint), ewin); g_signal_connect(ewin->lcd, "style-set", G_CALLBACK(screen_restyle), ewin); g_signal_connect(emuwin, "button-press-event", G_CALLBACK(mouse_press_event), ewin); g_signal_connect(emuwin, "motion-notify-event", G_CALLBACK(pointer_motion_event), ewin); g_signal_connect(emuwin, "button-release-event", G_CALLBACK(mouse_release_event), ewin); g_signal_connect(emuwin, "popup-menu", G_CALLBACK(popup_menu_event), ewin); /* FIXME: this is rather broken; GTK_DEST_DEFAULT_ALL sends a successful "finished" message to any drop that matches the list of targets. Files/URIs we can't accept should be rejected, and we shouldn't send the "finished" message until it's actually finished. */ gtk_drag_dest_set(emuwin, GTK_DEST_DEFAULT_ALL, NULL, 0, GDK_ACTION_COPY); gtk_drag_dest_add_uri_targets(emuwin); g_signal_connect(emuwin, "drag-data-received", G_CALLBACK(drag_data_received), ewin); /* Hint calculation assumes the emulator is the only thing in the window; if other widgets are added, this will have to change accordingly */ ewin->geomhints.min_width = minwidth; ewin->geomhints.min_height = minheight; a1 = (double) minwidth / minheight; a2 = (double) defwidth / defheight; ewin->geomhints.min_aspect = MIN(a1, a2) - 0.0001; ewin->geomhints.max_aspect = MAX(a1, a2) + 0.0001; ewin->geomhintmask = (GDK_HINT_MIN_SIZE | GDK_HINT_ASPECT); /* If the window is already realized, set the hints now, so that the WM will see the new hints before we try to resize the window */ set_size_hints(ewin->window, ewin); gtk_widget_set_size_request(emuwin, minwidth, minheight); gtk_container_add(GTK_CONTAINER(ewin->window), emuwin); if (!window_maximized(ewin)) gtk_window_resize(GTK_WINDOW(ewin->window), curwidth, curheight); gtk_widget_show_all(emuwin); if (err) { messagebox01(GTK_WINDOW(ewin->window), GTK_MESSAGE_ERROR, "Unable to load skin", "%s", err->message); g_error_free(err); } } static void window_destroy(G_GNUC_UNUSED GtkWidget *w, gpointer data) { TilemEmulatorWindow *ewin = data; if (!window_maximized(ewin)) tilem_config_set("settings", "zoom/r", ewin->zoom_factor, NULL); ewin->window = ewin->layout = ewin->lcd = ewin->background = NULL; } TilemEmulatorWindow *tilem_emulator_window_new(TilemCalcEmulator *emu) { TilemEmulatorWindow *ewin; g_return_val_if_fail(emu != NULL, NULL); ewin = g_slice_new0(TilemEmulatorWindow); ewin->emu = emu; tilem_config_get("settings", "skin_disabled/b", &ewin->skin_disabled, "smooth_scaling/b=1", &ewin->lcd_smooth_scale, "zoom/r=2.0", &ewin->zoom_factor, NULL); if (ewin->zoom_factor <= 1.0) ewin->zoom_factor = 1.0; /* Create the window */ ewin->window = gtk_window_new(GTK_WINDOW_TOPLEVEL); g_signal_connect(ewin->window, "window-state-event", G_CALLBACK(window_state_changed), ewin); g_signal_connect(ewin->window, "destroy", G_CALLBACK(window_destroy), ewin); g_signal_connect_after(ewin->window, "check-resize", G_CALLBACK(set_size_hints), ewin); gtk_widget_add_events(ewin->window, (GDK_KEY_PRESS_MASK | GDK_KEY_RELEASE_MASK)); g_signal_connect(ewin->window, "key-press-event", G_CALLBACK(key_press_event), ewin); g_signal_connect(ewin->window, "key-release-event", G_CALLBACK(key_release_event), ewin); build_menu(ewin); tilem_emulator_window_calc_changed(ewin); return ewin; } void tilem_emulator_window_free(TilemEmulatorWindow *ewin) { g_return_if_fail(ewin != NULL); if (ewin->lcd) gtk_widget_destroy(ewin->lcd); if (ewin->background) gtk_widget_destroy(ewin->background); if (ewin->layout) gtk_widget_destroy(ewin->layout); if (ewin->window) gtk_widget_destroy(ewin->window); if (ewin->popup_menu) gtk_widget_destroy(ewin->popup_menu); if (ewin->actions) g_object_unref(ewin->actions); g_free(ewin->lcd_image_buf); g_free(ewin->skin_file_name); if (ewin->skin) { skin_unload(ewin->skin); g_free(ewin->skin); } if (ewin->lcd_cmap) gdk_rgb_cmap_free(ewin->lcd_cmap); g_slice_free(TilemEmulatorWindow, ewin); } void tilem_emulator_window_set_skin(TilemEmulatorWindow *ewin, const char *filename) { g_return_if_fail(ewin != NULL); g_free(ewin->skin_file_name); if (filename) ewin->skin_file_name = g_strdup(filename); else ewin->skin_file_name = NULL; redraw_screen(ewin); } /* Switch between skin and LCD-only mode */ void tilem_emulator_window_set_skin_disabled(TilemEmulatorWindow *ewin, gboolean disabled) { g_return_if_fail(ewin != NULL); if (ewin->skin_disabled != !!disabled) { ewin->skin_disabled = !!disabled; redraw_screen(ewin); } } void tilem_emulator_window_calc_changed(TilemEmulatorWindow *ewin) { const char *model; char *name = NULL, *path; g_return_if_fail(ewin != NULL); g_return_if_fail(ewin->emu != NULL); g_free(ewin->skin_file_name); ewin->skin_file_name = NULL; if (!ewin->emu->calc) return; model = ewin->emu->calc->hw.name; tilem_config_get(model, "skin/f", &name, NULL); if (!name) name = g_strdup_printf("%s.skn", model); if (!g_path_is_absolute(name)) { path = get_shared_file_path("skins", name, NULL); tilem_emulator_window_set_skin(ewin, path); g_free(path); } else { tilem_emulator_window_set_skin(ewin, name); } g_free(name); } void tilem_emulator_window_refresh_lcd(TilemEmulatorWindow *ewin) { g_return_if_fail(ewin != NULL); if (ewin->lcd) gtk_widget_queue_draw(ewin->lcd); } /* Display the lcd image into the terminal */ void display_lcdimage_into_terminal(TilemEmulatorWindow *ewin) { int width, height; guchar* lcddata; int x, y; char c; width = ewin->emu->calc->hw.lcdwidth; height = ewin->emu->calc->hw.lcdheight; FILE* lcd_content_file; /* Alloc mmem */ lcddata = g_new(guchar, (width / 8) * height); /* Get the lcd content using the function 's pointer from Benjamin's core */ (*ewin->emu->calc->hw.get_lcd)(ewin->emu->calc, lcddata); /* Print a little demo just for fun;) */ printf("\n\n\n"); printf(" r rr r rrrrr rrr r rrrrr r r rr r rr r \n"); printf(" r r r r r r r r rr rr r r r r r r r \n"); printf(" r r r r r r r r r r r r r r r r r r \n"); printf("rrrrr r r r r r r rrrr r r r r r r r rrrrr rrrrr rrrrr \n"); printf(" r r r r r r r r r r rrr r r r r r r \n"); printf(" r r r r r r r r r r r r r r r r \n"); printf(" r rr r r rrr rrrrr rrrrr r r r rr r \n"); printf("\n(Here is just a sample...)\n\n"); /* Request user to know which char user wants */ printf("Which char to display FOR BLACK?\n"); scanf("%c", &c); /* Choose wich char for the black */ //printf("Which char to display FOR GRAY ?\n"); //scanf("%c", &b); /* Choose wich char for the black */ lcd_content_file = g_fopen("lcd_content.txt", "w"); printf("\n\n\n### LCD CONTENT ###\n\n\n\n"); for (y = 0; y < height; y++) { for (x = 0; x < width; x++) { /*printf("%d ", lcddata[y * width + x]); */ if (lcddata[(y * width + x) / 8] & (0x80 >> (x % 8))) { printf("%c", c); if(lcd_content_file != NULL) fprintf(lcd_content_file,"%c", c); } else { printf("%c", ' '); if(lcd_content_file != NULL) fprintf(lcd_content_file,"%c", ' '); } } printf("\n"); if(lcd_content_file != NULL) fprintf(lcd_content_file,"%c", '\n'); } if(lcd_content_file != NULL) { fclose(lcd_content_file); printf("\n### END ###\n\nSaved into lcd_content.txt\n"); } }