bsnes/phoenix/gtk/window.cpp

403 lines
14 KiB
C++
Raw Normal View History

namespace phoenix {
static gint Window_close(GtkWidget* widget, GdkEvent* event, Window* window) {
if(window->onClose) window->onClose();
else window->setVisible(false);
if(window->state.modal && !window->visible()) window->setModal(false);
return true;
}
static gboolean Window_expose(GtkWidget* widget, GdkEvent* event, Window* window) {
if(window->state.backgroundColorOverride == false) return false;
cairo_t* context = gdk_cairo_create(widget->window);
Color color = window->backgroundColor();
double red = (double)color.red / 255.0;
double green = (double)color.green / 255.0;
double blue = (double)color.blue / 255.0;
double alpha = (double)color.alpha / 255.0;
if(gdk_screen_is_composited(gdk_screen_get_default())
&& gdk_screen_get_rgba_colormap(gdk_screen_get_default())
) {
cairo_set_source_rgba(context, red, green, blue, alpha);
} else {
cairo_set_source_rgb(context, red, green, blue);
}
cairo_set_operator(context, CAIRO_OPERATOR_SOURCE);
cairo_paint(context);
cairo_destroy(context);
return false;
}
static gboolean Window_configure(GtkWidget* widget, GdkEvent* event, Window* window) {
if(gtk_widget_get_realized(window->p.widget) == false) return false;
Update to higan v091r14 and ananke v00r03 releases. byuu says: higan changelog: - generates title displayed in emulator window by asking the core - core builds title solely from "information/title" ... if it's not there, you don't get a title at all - sub-system load menu is gone ... since there are multiple revisions of the SGB, this never really worked well anyway - to load an SGB, BS-X or ST cartridge, load the base cartridge first - "File->Load Game" moved to "Load->Import Game" ... may cause a bit of confusion to new users, but I don't like having a single-item menu, we'll just have to explain it to new users - browser window redone to look like ananke - home button here goes to ~/Emulation rather than just ~ like ananke, since this is the home of game folders - game folder icon is now the executable icon for the Tango theme (orange diamond), meant to represent a complete game rather than a game file or archive ananke changelog: - outputs GBC games to "Game Boy Color/" instead of "Game Boy/" - adds the file basename to "information/title" Known issues: - using ananke to load a GB game trips the Super Famicom SGB mode and fails (need to make the full-path auto-detection ignore non-bootable systems) - need to dump and test some BS-X media before releasing - ananke lacks BS-X Satellaview cartridge support - v092 isn't going to let you retarget the ananke/higan game folder path of ~/Emulation, you will have to wait for a future version if that bothers you so greatly [Later, after the v092 release, byuu posted this additional changelog: - kill laevateinn - add title() - add bootable, remove load - combine file, library - combine [][][] paths - fix SFC subtype handling XML->BML - update file browser to use buttons - update file browser keyboard handling - update system XML->BML - fix sufami turbo hashing - remove Cartridge::manifest ]
2012-12-25 05:31:55 +00:00
if(window->visible() == false) return false;
GdkWindow *gdkWindow = gtk_widget_get_window(widget);
GdkRectangle border, client;
gdk_window_get_frame_extents(gdkWindow, &border);
gdk_window_get_geometry(gdkWindow, nullptr, nullptr, &client.width, &client.height, nullptr);
gdk_window_get_origin(gdkWindow, &client.x, &client.y);
if(window->state.fullScreen == false) {
//update geometry settings
settings->geometry.frameX = client.x - border.x;
settings->geometry.frameY = client.y - border.y;
settings->geometry.frameWidth = border.width - client.width;
settings->geometry.frameHeight = border.height - client.height;
Update to v085r08 release. byuu says: Changelog: - follow the Laevateinn topic to get most of it - also added NMI, IRQ step buttons to CPU debugger - also added trace masking + trace mask reset - also added memory export - cartridge loading is entirely folder-based now FitzRoy, I'll go ahead and make a second compromise with you for v086: I'll match the following: /path/to/SNES.sfc/*.sfc /path/to/NES.fc/*.prg, *.chr (split format) /path/to/NES.fc/*.fc (merged format) /path/to/GB.gb/*.gb /path/to/GBC.gbc/*.gbc Condition will be that there can only be one of each file. If there's more than one, it'll abort. That lets me name my ROMs as "Game.fc/Game.fc", and you can name yours as "Game.fc/cartridge.prg, cartridge.chr". Or whatever you want. We'll just go with that, see what fares out as the most popular, and then restrict it back to that method. The folder must have the .fc, etc extension though. That will be how we avoid false-positive folder matches. [Editor's note - the Laevateinn topic mentions these changes for v085r08: Added SMP/PPU breakpoints, SMP debugger, SMP stepping / tracing, memory editing on APU-bus / VRAM / OAM / CGRAM, save state menu, WRAM mirroring on breakpoints, protected MMIO memory regions (otherwise, viewing $002100 could crash your game.) Major missing components: - trace mask - trace mask clear / usage map clear - window geometry caching / sizing improvements - VRAM viewer - properties viewer - working memory export button The rest will most likely appear after v086 is released. ]
2012-02-12 05:35:40 +00:00
if(window->state.backgroundColorOverride == false) {
GdkColor color = widget->style->bg[GTK_STATE_NORMAL];
settings->window.backgroundColor
Update to v085r08 release. byuu says: Changelog: - follow the Laevateinn topic to get most of it - also added NMI, IRQ step buttons to CPU debugger - also added trace masking + trace mask reset - also added memory export - cartridge loading is entirely folder-based now FitzRoy, I'll go ahead and make a second compromise with you for v086: I'll match the following: /path/to/SNES.sfc/*.sfc /path/to/NES.fc/*.prg, *.chr (split format) /path/to/NES.fc/*.fc (merged format) /path/to/GB.gb/*.gb /path/to/GBC.gbc/*.gbc Condition will be that there can only be one of each file. If there's more than one, it'll abort. That lets me name my ROMs as "Game.fc/Game.fc", and you can name yours as "Game.fc/cartridge.prg, cartridge.chr". Or whatever you want. We'll just go with that, see what fares out as the most popular, and then restrict it back to that method. The folder must have the .fc, etc extension though. That will be how we avoid false-positive folder matches. [Editor's note - the Laevateinn topic mentions these changes for v085r08: Added SMP/PPU breakpoints, SMP debugger, SMP stepping / tracing, memory editing on APU-bus / VRAM / OAM / CGRAM, save state menu, WRAM mirroring on breakpoints, protected MMIO memory regions (otherwise, viewing $002100 could crash your game.) Major missing components: - trace mask - trace mask clear / usage map clear - window geometry caching / sizing improvements - VRAM viewer - properties viewer - working memory export button The rest will most likely appear after v086 is released. ]
2012-02-12 05:35:40 +00:00
= ((uint8_t)(color.red >> 8) << 16)
+ ((uint8_t)(color.green >> 8) << 8)
+ ((uint8_t)(color.blue >> 8) << 0);
}
2011-11-17 12:05:35 +00:00
settings->save();
}
Geometry geometry = {
client.x,
client.y + window->p.menuHeight(),
client.width,
client.height - window->p.menuHeight() - window->p.statusHeight()
};
//move
if(geometry.x != window->state.geometry.x || geometry.y != window->state.geometry.y) {
if(window->state.fullScreen == false) {
window->state.geometry.x = geometry.x;
window->state.geometry.y = geometry.y;
}
if(window->p.locked == false && window->onMove) window->onMove();
}
//size
if(geometry.width != window->state.geometry.width || geometry.height != window->state.geometry.height) {
window->p.onSizePending = true;
}
return false;
}
static void Window_drop(GtkWidget* widget, GdkDragContext* context, gint x, gint y,
GtkSelectionData* data, guint type, guint timestamp, Window* window) {
if(window->state.droppable == false) return;
lstring paths = DropPaths(data);
if(paths.empty()) return;
if(window->onDrop) window->onDrop(paths);
}
static gboolean Window_keyPress(GtkWidget* widget, GdkEventKey* event, Window* window) {
Keyboard::Keycode key = Keysym(event->keyval);
if(key != Keyboard::Keycode::None && window->onKeyPress) window->onKeyPress(key);
return false;
}
static gboolean Window_keyRelease(GtkWidget* widget, GdkEventKey* event, Window* window) {
Keyboard::Keycode key = Keysym(event->keyval);
if(key != Keyboard::Keycode::None && window->onKeyRelease) window->onKeyRelease(key);
return false;
}
static void Window_sizeAllocate(GtkWidget* widget, GtkAllocation* allocation, Window* window) {
//size-allocate sent from gtk_fixed_move(); detect if layout unchanged and return
if(allocation->width == window->p.lastAllocation.width
&& allocation->height == window->p.lastAllocation.height) return;
window->state.geometry.width = allocation->width;
window->state.geometry.height = allocation->height;
for(auto& layout : window->state.layout) {
Geometry geometry = window->geometry();
geometry.x = geometry.y = 0;
layout.setGeometry(geometry);
}
if(window->p.onSizePending && window->p.locked == false && window->onSize) {
window->p.onSizePending = false;
window->onSize();
}
window->p.lastAllocation = *allocation;
}
static void Window_sizeRequest(GtkWidget* widget, GtkRequisition* requisition, Window* window) {
requisition->width = window->state.geometry.width;
requisition->height = window->state.geometry.height;
}
Window& pWindow::none() {
static Window* window = nullptr;
if(window == nullptr) window = new Window;
return *window;
}
void pWindow::append(Layout& layout) {
Geometry geometry = this->geometry();
geometry.x = geometry.y = 0;
layout.setGeometry(geometry);
}
void pWindow::append(Menu& menu) {
if(window.state.menuFont) menu.p.setFont(window.state.menuFont);
else menu.p.setFont(Font::sans(8));
gtk_menu_shell_append(GTK_MENU_SHELL(this->menu), menu.p.widget);
gtk_widget_show(menu.p.widget);
}
void pWindow::append(Widget& widget) {
if(widget.font().empty() && !window.state.widgetFont.empty()) {
widget.setFont(window.state.widgetFont);
}
if(GetParentWidget(&widget)) {
widget.p.gtkParent = GetParentWidget(&widget)->p.container(widget);
} else {
widget.p.gtkParent = formContainer;
}
gtk_fixed_put(GTK_FIXED(widget.p.gtkParent), widget.p.gtkWidget, 0, 0);
if(widget.state.font) widget.p.setFont(widget.state.font);
else if(window.state.widgetFont) widget.p.setFont(window.state.widgetFont);
else widget.p.setFont(Font::sans(8));
widget.setVisible(widget.visible());
}
Geometry pWindow::frameMargin() {
if(window.state.fullScreen) return {
0,
menuHeight(),
0,
menuHeight() + statusHeight()
};
return {
settings->geometry.frameX,
settings->geometry.frameY + menuHeight(),
settings->geometry.frameWidth,
settings->geometry.frameHeight + menuHeight() + statusHeight()
};
}
bool pWindow::focused() {
return gtk_window_is_active(GTK_WINDOW(widget));
}
Geometry pWindow::geometry() {
if(window.state.fullScreen) {
int x, y, width, height;
gtk_window_get_position(GTK_WINDOW(widget), &x, &y);
gtk_window_get_size(GTK_WINDOW(widget), &width, &height);
return {x, y + menuHeight(), width, height - menuHeight() - statusHeight()};
}
return window.state.geometry;
}
void pWindow::remove(Layout& layout) {
}
void pWindow::remove(Menu& menu) {
menu.p.orphan();
}
void pWindow::remove(Widget& widget) {
widget.p.orphan();
}
void pWindow::setBackgroundColor(Color color) {
GdkColor gdkColor = CreateColor(color.red, color.green, color.blue);
gtk_widget_modify_bg(widget, GTK_STATE_NORMAL, &gdkColor);
}
void pWindow::setDroppable(bool droppable) {
gtk_drag_dest_set(widget, GTK_DEST_DEFAULT_ALL, nullptr, 0, GDK_ACTION_COPY);
if(droppable) gtk_drag_dest_add_uri_targets(widget);
}
void pWindow::setFocused() {
gtk_window_present(GTK_WINDOW(widget));
}
void pWindow::setFullScreen(bool fullScreen) {
if(fullScreen == false) {
gtk_window_unfullscreen(GTK_WINDOW(widget));
} else {
gtk_window_fullscreen(GTK_WINDOW(widget));
/*unsigned monitor = gdk_screen_get_monitor_at_window(gdk_screen_get_default(), gtk_widget_get_window(widget));
GdkRectangle rectangle = {0};
gdk_screen_get_monitor_geometry(gdk_screen_get_default(), monitor, &rectangle);
gtk_window_set_decorated(GTK_WINDOW(widget), false);
gtk_window_move(GTK_WINDOW(widget), rectangle.x, rectangle.y);
gtk_window_resize(GTK_WINDOW(widget), rectangle.width, rectangle.height);
gtk_widget_set_size_request(formContainer, rectangle.width, rectangle.height);*/
}
}
void pWindow::setGeometry(Geometry geometry) {
Geometry margin = frameMargin();
gtk_window_move(GTK_WINDOW(widget), geometry.x - margin.x, geometry.y - margin.y);
GdkGeometry geom;
geom.min_width = window.state.resizable ? 1 : window.state.geometry.width;
geom.min_height = window.state.resizable ? 1 : window.state.geometry.height;
gtk_window_set_geometry_hints(GTK_WINDOW(widget), GTK_WIDGET(widget), &geom, GDK_HINT_MIN_SIZE);
//gtk_window_set_policy(GTK_WINDOW(widget), true, true, false);
gtk_widget_set_size_request(formContainer, geometry.width, geometry.height);
gtk_window_resize(GTK_WINDOW(widget), geometry.width, geometry.height + menuHeight() + statusHeight());
for(auto& layout : window.state.layout) {
Geometry layoutGeometry = geometry;
layoutGeometry.x = layoutGeometry.y = 0;
layout.setGeometry(layoutGeometry);
}
}
void pWindow::setMenuFont(string font) {
for(auto& item : window.state.menu) item.p.setFont(font);
}
void pWindow::setMenuVisible(bool visible) {
gtk_widget_set_visible(menu, visible);
}
Update to v088r11 release. byuu says: Changelog: - phoenix has added Window::setModal(bool modal = true); - file dialog is now modal. This allows emulation cores to request data and get it immediately before continuing the loading process - save data is hooked up for most systems, still need to handle subsystem slot saves (Sufami Turbo, basically.) - toggle fullscreen key binding added (Alt+Enter for now. I think F11 is probably better though, Enter is often mapped to game start button.) - video scaling is in (center, scale, stretch), works the same in windowed and fullscreen mode (stretch hides resize window option), all in the settings menu now - enough structure to map all saved paths for the browser and to load BS-X slotted carts, BS-X carts, single Sufami Turbo carts Caveats / Missing: - Super Game Boy input doesn't work yet (due to change in callback binding) - doesn't load secondary Sufami Turbo slot yet - BS-X BIOS isn't show the data pack games to load for some reason (ugh, I hate the shit out of debugging BS-X stuff ...) - need mute audio, sync audio+video toggle, save/load state menu and quick keys, XML mapping information window - need cheat editor and cheat database - need state manager - need to sort subsystems below main systems in load menu (basically just see if media.slot.size() > 0) - need video shaders (will probably leave off filters for the time being ... due to that 24/30-bit thing) - need video adjustments (contrast etc, overscan masks) - need audio adjustments (frequency, latency, resampler, volume, per-system frequency) - need driver selection and input focus policy (driver crash detection would be nice too) - need NSS DIP switch settings (that one will be really fun) - need to save and load window geometry settings - need to hook up controller selection (won't be fun), create a map to hide controllers with no inputs to reassign
2012-05-03 12:36:47 +00:00
void pWindow::setModal(bool modal) {
if(modal == true) {
gtk_window_set_modal(GTK_WINDOW(widget), true);
while(window.state.modal) {
Application::processEvents();
usleep(20 * 1000);
}
gtk_window_set_modal(GTK_WINDOW(widget), false);
}
Update to v088r11 release. byuu says: Changelog: - phoenix has added Window::setModal(bool modal = true); - file dialog is now modal. This allows emulation cores to request data and get it immediately before continuing the loading process - save data is hooked up for most systems, still need to handle subsystem slot saves (Sufami Turbo, basically.) - toggle fullscreen key binding added (Alt+Enter for now. I think F11 is probably better though, Enter is often mapped to game start button.) - video scaling is in (center, scale, stretch), works the same in windowed and fullscreen mode (stretch hides resize window option), all in the settings menu now - enough structure to map all saved paths for the browser and to load BS-X slotted carts, BS-X carts, single Sufami Turbo carts Caveats / Missing: - Super Game Boy input doesn't work yet (due to change in callback binding) - doesn't load secondary Sufami Turbo slot yet - BS-X BIOS isn't show the data pack games to load for some reason (ugh, I hate the shit out of debugging BS-X stuff ...) - need mute audio, sync audio+video toggle, save/load state menu and quick keys, XML mapping information window - need cheat editor and cheat database - need state manager - need to sort subsystems below main systems in load menu (basically just see if media.slot.size() > 0) - need video shaders (will probably leave off filters for the time being ... due to that 24/30-bit thing) - need video adjustments (contrast etc, overscan masks) - need audio adjustments (frequency, latency, resampler, volume, per-system frequency) - need driver selection and input focus policy (driver crash detection would be nice too) - need NSS DIP switch settings (that one will be really fun) - need to save and load window geometry settings - need to hook up controller selection (won't be fun), create a map to hide controllers with no inputs to reassign
2012-05-03 12:36:47 +00:00
}
void pWindow::setResizable(bool resizable) {
gtk_window_set_resizable(GTK_WINDOW(widget), resizable);
gtk_statusbar_set_has_resize_grip(GTK_STATUSBAR(status), resizable);
}
void pWindow::setStatusFont(string font) {
pFont::setFont(status, font);
}
void pWindow::setStatusText(string text) {
gtk_statusbar_pop(GTK_STATUSBAR(status), 1);
gtk_statusbar_push(GTK_STATUSBAR(status), 1, text);
}
void pWindow::setStatusVisible(bool visible) {
gtk_widget_set_visible(status, visible);
}
void pWindow::setTitle(string text) {
gtk_window_set_title(GTK_WINDOW(widget), text);
}
void pWindow::setVisible(bool visible) {
gtk_widget_set_visible(widget, visible);
if(visible) {
if(gtk_widget_get_visible(menu)) {
GtkAllocation allocation;
gtk_widget_get_allocation(menu, &allocation);
settings->geometry.menuHeight = allocation.height;
}
if(gtk_widget_get_visible(status)) {
GtkAllocation allocation;
gtk_widget_get_allocation(status, &allocation);
settings->geometry.statusHeight = allocation.height;
}
}
}
void pWindow::setWidgetFont(string font) {
}
void pWindow::constructor() {
lastAllocation.width = 0;
lastAllocation.height = 0;
onSizePending = false;
widget = gtk_window_new(GTK_WINDOW_TOPLEVEL);
//if program was given a name, try and set the window taskbar icon from one of the pixmaps folders
if(applicationState.name.empty() == false) {
string filename = {"/usr/share/pixmaps/", applicationState.name, ".png"};
if(!file::exists(filename)) filename = {"/usr/local/share/pixmaps/", applicationState.name, ".png"};
if(file::exists(filename)) {
//maximum image size supported by GTK+ is 256x256; so we must scale larger images ourselves
nall::image icon(filename);
icon.scale(min(256u, icon.width), min(256u, icon.height), Interpolation::Hermite);
GdkPixbuf* pixbuf = CreatePixbuf(icon);
gtk_window_set_icon(GTK_WINDOW(widget), pixbuf);
g_object_unref(G_OBJECT(pixbuf));
}
}
GdkColormap* colormap = gdk_screen_get_rgba_colormap(gdk_screen_get_default());
if(!colormap) colormap = gdk_screen_get_rgb_colormap(gdk_screen_get_default());
if(colormap) gtk_widget_set_colormap(widget, colormap);
gtk_window_set_resizable(GTK_WINDOW(widget), true);
#if GTK_MAJOR_VERSION >= 3
gtk_window_set_has_resize_grip(GTK_WINDOW(widget), false);
#endif
gtk_widget_set_app_paintable(widget, true);
gtk_widget_add_events(widget, GDK_CONFIGURE);
menuContainer = gtk_vbox_new(false, 0);
gtk_container_add(GTK_CONTAINER(widget), menuContainer);
gtk_widget_show(menuContainer);
menu = gtk_menu_bar_new();
gtk_box_pack_start(GTK_BOX(menuContainer), menu, false, false, 0);
formContainer = gtk_fixed_new();
gtk_box_pack_start(GTK_BOX(menuContainer), formContainer, true, true, 0);
gtk_widget_show(formContainer);
statusContainer = gtk_event_box_new();
status = gtk_statusbar_new();
gtk_statusbar_set_has_resize_grip(GTK_STATUSBAR(status), true);
gtk_container_add(GTK_CONTAINER(statusContainer), status);
gtk_box_pack_start(GTK_BOX(menuContainer), statusContainer, false, false, 0);
gtk_widget_show(statusContainer);
setTitle("");
setResizable(window.state.resizable);
setGeometry(window.state.geometry);
setMenuFont(Font::sans(8));
setStatusFont(Font::sans(8));
g_signal_connect(G_OBJECT(widget), "delete-event", G_CALLBACK(Window_close), (gpointer)&window);
g_signal_connect(G_OBJECT(widget), "expose-event", G_CALLBACK(Window_expose), (gpointer)&window);
g_signal_connect(G_OBJECT(widget), "configure-event", G_CALLBACK(Window_configure), (gpointer)&window);
g_signal_connect(G_OBJECT(widget), "drag-data-received", G_CALLBACK(Window_drop), (gpointer)&window);
g_signal_connect(G_OBJECT(widget), "key-press-event", G_CALLBACK(Window_keyPress), (gpointer)&window);
g_signal_connect(G_OBJECT(widget), "key-release-event", G_CALLBACK(Window_keyPress), (gpointer)&window);
g_signal_connect(G_OBJECT(formContainer), "size-allocate", G_CALLBACK(Window_sizeAllocate), (gpointer)&window);
g_signal_connect(G_OBJECT(formContainer), "size-request", G_CALLBACK(Window_sizeRequest), (gpointer)&window);
window.state.backgroundColor = Color(
(uint8_t)(settings->window.backgroundColor >> 16),
(uint8_t)(settings->window.backgroundColor >> 8),
(uint8_t)(settings->window.backgroundColor >> 0)
);
}
unsigned pWindow::menuHeight() {
return window.state.menuVisible ? settings->geometry.menuHeight : 0;
}
unsigned pWindow::statusHeight() {
return window.state.statusVisible ? settings->geometry.statusHeight : 0;
}
}