Xemu [doxygen]  hyppo 0a42be3a057156924bc1b626a687bd6e27349c45 @ Sat 19 Mar 02:15:11 CET 2022
gui_win.c
Go to the documentation of this file.
1 /* Part of the Xemu project, please visit: https://github.com/lgblgblgb/xemu
2  ~/xemu/gui/gui_win.c: UI implementation for Windows of Xemu's UI abstraction layer
3  Copyright (C)2016-2022 LGB (Gábor Lénárt) <lgblgblgb@gmail.com>
4 
5 This program is free software; you can redistribute it and/or modify
6 it under the terms of the GNU General Public License as published by
7 the Free Software Foundation; either version 2 of the License, or
8 (at your option) any later version.
9 
10 This program is distributed in the hope that it will be useful,
11 but WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 GNU General Public License for more details.
14 
15 You should have received a copy of the GNU General Public License
16 along with this program; if not, write to the Free Software
17 Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */
18 
19 
20 /* ---------------------------------------- Windows STUFFS based on Win32 native APIs ---------------------------------------- */
21 
22 #include <windows.h>
23 #include <SDL_syswm.h>
24 
25 #ifndef GUI_HAS_POPUP
26 #define GUI_HAS_POPUP
27 #endif
28 
29 static struct {
34  HWND win_hwnd;
35  int problem;
36 } xemuwinmenu;
37 
38 
39 static int xemuwingui_init ( void )
40 {
41  SDL_SysWMinfo info;
42  SDL_VERSION(&info.version);
43  SDL_GetWindowWMInfo(sdl_win, &info);
44  xemuwinmenu.win_hwnd = info.info.win.window;
45  is_xemugui_ok = 1;
46  xemuwinmenu.num_of_hmenus = 0;
47  xemuwinmenu.num_of_items = 0;
48  return 0;
49 }
50 
51 
52 #if 0
53 static int xemuwingui_iteration ( void )
54 {
55  return 0;
56 }
57 #endif
58 
59 static int xemuwingui_file_selector ( int dialog_mode, const char *dialog_title, char *default_dir, char *selected, int path_max_size )
60 {
61  int res;
62  OPENFILENAME ofn; // common dialog box structure
63  ZeroMemory(&ofn, sizeof ofn);
64  ofn.lStructSize = sizeof ofn;
65  ofn.hwndOwner = xemuwinmenu.win_hwnd; // FIXME: it should be this way, though it seems sometimes works better with the value 0 ...
66  ofn.lpstrFile = selected;
67  *selected = '\0'; // sets to zero, since it seems windows dialog handler also used this as input?
68  ofn.nMaxFile = path_max_size;
69  ofn.lpstrFilter = NULL; // "All\0*.*\0Text\0*.TXT\0";
70  ofn.nFilterIndex = 0; // 1
71  ofn.lpstrFileTitle = NULL;
72  ofn.nMaxFileTitle = 0;
73  ofn.lpstrInitialDir = default_dir ? default_dir : NULL;
74  ofn.lpstrTitle = dialog_title;
75  switch (dialog_mode & 3) {
76  case XEMUGUI_FSEL_OPEN:
77  ofn.Flags = OFN_PATHMUSTEXIST | OFN_FILEMUSTEXIST | OFN_EXPLORER | OFN_HIDEREADONLY | OFN_NOCHANGEDIR;
78  res = !GetOpenFileName(&ofn);
79  break;
80  case XEMUGUI_FSEL_SAVE:
81  ofn.Flags = OFN_PATHMUSTEXIST | OFN_OVERWRITEPROMPT | OFN_EXPLORER | OFN_HIDEREADONLY | OFN_NOCHANGEDIR;
82  res = !GetSaveFileName(&ofn);
83  break;
84  default:
85  FATAL("Bad dialog_mode in file selector");
86  }
87  if (res) {
88  int err = CommDlgExtendedError();
89  *selected = '\0';
90  DEBUGGUI("GUI: file selector (Windows) error code: %04Xh for HWND owner %p" NL, err, ofn.hwndOwner);
91  if (err)
92  ERROR_WINDOW("Windows CommDlgExtendedError: %04Xh for HWND owner %p", err, ofn.hwndOwner);
93  } else
94  store_dir_from_file_selection(default_dir, selected, dialog_mode);
95  //xemunativegui_iteration();
97  return res;
98 }
99 
100 
101 
102 
103 static HMENU _wingui_recursive_menu_builder ( const struct menu_st desc[], const char *parent_name )
104 {
105  HMENU menu = CreatePopupMenu();
106  if (!menu) {
107  ERROR_WINDOW("CreatePopupMenu() failed in menu builder!");
108  goto PROBLEM;
109  }
110  if (xemuwinmenu.num_of_hmenus >= XEMUGUI_MAX_SUBMENUS)
111  FATAL("GUI: too many submenus!");
112  xemuwinmenu.hmenus[xemuwinmenu.num_of_hmenus++] = menu;
113  int radio_begin = xemuwinmenu.num_of_items;
114  int radio_active = xemuwinmenu.num_of_items; // radio active is a kinda odd name, but never mind ...
115  for (int a = 0; desc[a].name; a++) {
116  // Some sanity checks:
117  if (
118  ((desc[a].type & 0xFF) != XEMUGUI_MENUID_SUBMENU && !desc[a].handler) ||
119  ((desc[a].type & 0xFF) == XEMUGUI_MENUID_SUBMENU && (desc[a].handler || !desc[a].user_data)) ||
120  !desc[a].name
121  ) {
122  DEBUGPRINT("GUI: invalid menu entry found, skipping it (item #%d of menu \"%s\")" NL, a, parent_name);
123  continue;
124  }
125  if (xemuwinmenu.num_of_items >= XEMUGUI_MAX_ITEMS)
126  FATAL("GUI: too many items in menu builder!");
127  int ret = 1, type = desc[a].type;
128  switch (type & 0xFF) {
129  case XEMUGUI_MENUID_SUBMENU: {
130  // submenus use the user_data as the submenu menu_st struct pointer!
131  HMENU submenu = _wingui_recursive_menu_builder(desc[a].user_data, desc[a].name); // that's a prime example for using recursion :)
132  if (!submenu)
133  goto PROBLEM;
134  ret = AppendMenu(menu, MF_POPUP, (UINT_PTR)submenu, desc[a].name);
135  }
136  break;
139  DEBUGGUI("GUI: query-back for \"%s\"" NL, desc[a].name);
140  ((xemugui_callback_t)(desc[a].handler))(&desc[a], &type);
141  }
143  continue;
144  xemuwinmenu.items[xemuwinmenu.num_of_items] = &desc[a];
145  // Note the +1 for ID. That is because some stange Windows sting:
146  // TrackPopupMenu() with TPM_RETURNCMD returns zero as error/no-selection ...
147  // So we want to being with '1' that's why the PRE-incrementation instead of the POST
148  ret = AppendMenu(menu, MF_STRING, ++xemuwinmenu.num_of_items, desc[a].name);
149  break;
150  default:
151  DEBUGPRINT("GUI: invalid menu item type: %d (item #%d of menu \"%s\")" NL, type & 0xFF, a, parent_name);
152  break;
153  }
154  if (!ret) {
155  ERROR_WINDOW("AppendMenu() failed in menu builder!");
156  goto PROBLEM;
157  }
159  CheckMenuItem(menu, xemuwinmenu.num_of_items, MF_CHECKED);
161  AppendMenu(menu, MF_SEPARATOR, 0, NULL);
163  radio_begin = xemuwinmenu.num_of_items;
164  radio_active = xemuwinmenu.num_of_items;
165  }
167  radio_active = xemuwinmenu.num_of_items;
169  CheckMenuRadioItem(menu, radio_begin, xemuwinmenu.num_of_items, radio_active, MF_BYCOMMAND);
170  radio_begin = xemuwinmenu.num_of_items;
171  radio_active = xemuwinmenu.num_of_items;
172  }
173  }
174  return menu;
175 PROBLEM:
176  xemuwinmenu.problem = 1;
177  return NULL;
178 }
179 
180 
181 static void _wingui_destroy_menu ( void )
182 {
183  while (xemuwinmenu.num_of_hmenus > 0) {
184  int ret = DestroyMenu(xemuwinmenu.hmenus[--xemuwinmenu.num_of_hmenus]);
185  DEBUGGUI("GUI: destroyed menu at %p, retval = %d" NL, xemuwinmenu.hmenus[xemuwinmenu.num_of_hmenus], ret);
186  }
187  xemuwinmenu.num_of_hmenus = 0;
188  xemuwinmenu.num_of_items = 0;
189 }
190 
191 
192 static HMENU _wingui_create_popup_menu ( const struct menu_st desc[] )
193 {
194  _wingui_destroy_menu();
195  xemuwinmenu.problem = 0;
196  HMENU menu = _wingui_recursive_menu_builder(desc, XEMUGUI_MAINMENU_NAME);
197  if (!menu || xemuwinmenu.problem) {
198  _wingui_destroy_menu();
199  return NULL;
200  } else
201  return menu;
202 }
203 
204 
205 static inline void _wingui_getmousecoords ( POINT *point )
206 {
207  int x, y;
208  SDL_GetMouseState(&x, &y);
209  point->x = x; // we must play with these, since Windows uses point struct members type long (or WTF and why ...), rather than int ...
210  point->y = y;
211 }
212 
213 
214 static void _wingui_callback ( const struct menu_st *item )
215 {
216  DEBUGGUI("Return value = %s" NL, item->name);
217  if ((item->type & 0xFF) == XEMUGUI_MENUID_CALLABLE && item->handler)
218  ((xemugui_callback_t)(item->handler))(item, NULL);
219 }
220 
221 
222 // Compared to the GTK version, it seems Windows is unable to do async GUI so it's a blocking implementation :(
223 static int xemuwingui_popup ( const struct menu_st desc[] )
224 {
225  DEBUGGUI("GUI: WIN: popup!" NL);
226  HMENU menu = _wingui_create_popup_menu(desc);
227  if (!menu)
228  return 1;
229  int num_of_items = xemuwinmenu.num_of_items;
230  POINT point;
231  _wingui_getmousecoords(&point);
232  if (!ClientToScreen(xemuwinmenu.win_hwnd, &point)) {
233  // ClientToScreen returns with non-zero if it's OK!!!!
234  _wingui_destroy_menu();
235  ERROR_WINDOW("ClientToScreen returned with zero");
236  return 1;
237  }
238  int n = TrackPopupMenu(menu, TPM_RIGHTBUTTON | TPM_RETURNCMD | TPM_NONOTIFY, point.x, point.y, 0, xemuwinmenu.win_hwnd, NULL); // TPM_RETURNCMD causes to return with the selected ID (ie the 3rd param of AppendMenu)
240  DEBUGGUI("Returned: items=%d RETVAL=%d" NL, num_of_items, n);
241  // again, do not forget that IDs are from one in windows, since zero means non-selection or error!
242  if (n > 0 && n <= num_of_items) {
243  const struct menu_st *item = xemuwinmenu.items[n - 1];
244  _wingui_destroy_menu();
245  _wingui_callback(item);
246  } else
247  _wingui_destroy_menu();
249  return 0;
250 }
251 
252 
253 
254 
255 static const struct xemugui_descriptor_st xemuwingui_descriptor = {
256  .name = "windows",
257  .description = "Windows API based Xemu UI implementation",
258  .init = xemuwingui_init,
259  .shutdown = NULL, // we don't need shutdown for windows?
260  .iteration = NULL, // we don't need iteration for windows?
261  .file_selector = xemuwingui_file_selector,
262  .popup = xemuwingui_popup,
263  .info = NULL
264 };
XEMUGUI_MENUFLAG_END_RADIO
#define XEMUGUI_MENUFLAG_END_RADIO
Definition: emutools_gui.h:36
XEMUGUI_MENUFLAG_BEGIN_RADIO
#define XEMUGUI_MENUFLAG_BEGIN_RADIO
Definition: emutools_gui.h:35
menu_st
Definition: emutools_gui.h:65
sdl_win
SDL_Window * sdl_win
Definition: screen.c:43
hmenus
HMENU hmenus[XEMUGUI_MAX_SUBMENUS]
Definition: gui_win.c:32
XEMUGUI_MENUFLAG_ACTIVE_RADIO
#define XEMUGUI_MENUFLAG_ACTIVE_RADIO
Definition: emutools_gui.h:37
menu_st::user_data
const void * user_data
Definition: emutools_gui.h:69
XEMUGUI_MAX_SUBMENUS
#define XEMUGUI_MAX_SUBMENUS
Definition: emutools_gui.h:45
items
const struct menu_st * items[XEMUGUI_MAX_ITEMS]
Definition: gui_win.c:33
menu_st::type
int type
Definition: emutools_gui.h:67
menu_st::handler
const xemugui_callback_t handler
Definition: emutools_gui.h:68
xemu_drop_events
#define xemu_drop_events
Definition: gui.c:19
XEMUGUI_MENUID_CALLABLE
#define XEMUGUI_MENUID_CALLABLE
Definition: emutools_gui.h:30
xemugui_callback_t
void(* xemugui_callback_t)(const struct menu_st *desc, int *query)
Definition: emutools_gui.h:63
xemugui_descriptor_st::name
const char * name
Definition: emutools_gui.c:45
XEMUGUI_MENUFLAG_QUERYBACK
#define XEMUGUI_MENUFLAG_QUERYBACK
Definition: emutools_gui.h:40
XEMUGUI_FSEL_SAVE
#define XEMUGUI_FSEL_SAVE
Definition: emutools_gui.h:27
num_of_items
int num_of_items
Definition: gui_win.c:31
problem
int problem
Definition: gui_win.c:35
x
int x
Definition: console.c:27
DEBUGPRINT
#define DEBUGPRINT(...)
Definition: emutools_basicdefs.h:171
ERROR_WINDOW
#define ERROR_WINDOW(...)
Definition: xep128.h:116
NL
#define NL
Definition: fat32.c:37
XEMUGUI_FSEL_OPEN
#define XEMUGUI_FSEL_OPEN
Definition: emutools_gui.h:26
xemugui_descriptor_st
Definition: emutools_gui.c:44
menu_st::name
const char * name
Definition: emutools_gui.h:66
win_hwnd
HWND win_hwnd
Definition: gui_win.c:34
XEMUGUI_MENUID_SUBMENU
#define XEMUGUI_MENUID_SUBMENU
Definition: emutools_gui.h:31
y
int y
Definition: console.c:27
XEMUGUI_MENUFLAG_HIDDEN
#define XEMUGUI_MENUFLAG_HIDDEN
Definition: emutools_gui.h:42
name
const char * name
Definition: joystick.c:46
is_xemugui_ok
int is_xemugui_ok
Definition: emutools_gui.c:22
XEMUGUI_MAX_ITEMS
#define XEMUGUI_MAX_ITEMS
Definition: emutools_gui.h:48
XEMUGUI_MENUFLAG_CHECKED
#define XEMUGUI_MENUFLAG_CHECKED
Definition: emutools_gui.h:39
FATAL
#define FATAL(...)
Definition: xep128.h:117
XEMUGUI_MAINMENU_NAME
#define XEMUGUI_MAINMENU_NAME
Definition: emutools_gui.h:52
num_of_hmenus
int num_of_hmenus
Definition: gui_win.c:30
DEBUGGUI
#define DEBUGGUI
Definition: emutools_gui.h:22
XEMUGUI_MENUFLAG_SEPARATOR
#define XEMUGUI_MENUFLAG_SEPARATOR
Definition: emutools_gui.h:38