gtk.c 75 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254125512561257125812591260126112621263126412651266126712681269127012711272127312741275127612771278127912801281128212831284128512861287128812891290129112921293129412951296129712981299130013011302130313041305130613071308130913101311131213131314131513161317131813191320132113221323132413251326132713281329133013311332133313341335133613371338133913401341134213431344134513461347134813491350135113521353135413551356135713581359136013611362136313641365136613671368136913701371137213731374137513761377137813791380138113821383138413851386138713881389139013911392139313941395139613971398139914001401140214031404140514061407140814091410141114121413141414151416141714181419142014211422142314241425142614271428142914301431143214331434143514361437143814391440144114421443144414451446144714481449145014511452145314541455145614571458145914601461146214631464146514661467146814691470147114721473147414751476147714781479148014811482148314841485148614871488148914901491149214931494149514961497149814991500150115021503150415051506150715081509151015111512151315141515151615171518151915201521152215231524152515261527152815291530153115321533153415351536153715381539154015411542154315441545154615471548154915501551155215531554155515561557155815591560156115621563156415651566156715681569157015711572157315741575157615771578157915801581158215831584158515861587158815891590159115921593159415951596159715981599160016011602160316041605160616071608160916101611161216131614161516161617161816191620162116221623162416251626162716281629163016311632163316341635163616371638163916401641164216431644164516461647164816491650165116521653165416551656165716581659166016611662166316641665166616671668166916701671167216731674167516761677167816791680168116821683168416851686168716881689169016911692169316941695169616971698169917001701170217031704170517061707170817091710171117121713171417151716171717181719172017211722172317241725172617271728172917301731173217331734173517361737173817391740174117421743174417451746174717481749175017511752175317541755175617571758175917601761176217631764176517661767176817691770177117721773177417751776177717781779178017811782178317841785178617871788178917901791179217931794179517961797179817991800180118021803180418051806180718081809181018111812181318141815181618171818181918201821182218231824182518261827182818291830183118321833183418351836183718381839184018411842184318441845184618471848184918501851185218531854185518561857185818591860186118621863186418651866186718681869187018711872187318741875187618771878187918801881188218831884188518861887188818891890189118921893189418951896189718981899190019011902190319041905190619071908190919101911191219131914191519161917191819191920192119221923192419251926192719281929193019311932193319341935193619371938193919401941194219431944194519461947194819491950195119521953195419551956195719581959196019611962196319641965196619671968196919701971197219731974197519761977197819791980198119821983198419851986198719881989199019911992199319941995199619971998199920002001200220032004200520062007200820092010201120122013201420152016201720182019202020212022202320242025202620272028202920302031203220332034203520362037203820392040204120422043204420452046204720482049205020512052205320542055205620572058205920602061206220632064206520662067206820692070207120722073207420752076207720782079208020812082208320842085208620872088208920902091209220932094209520962097209820992100210121022103210421052106210721082109211021112112211321142115211621172118211921202121212221232124212521262127212821292130213121322133213421352136213721382139214021412142214321442145214621472148214921502151215221532154215521562157215821592160216121622163216421652166216721682169217021712172217321742175217621772178217921802181218221832184218521862187218821892190219121922193219421952196219721982199220022012202220322042205220622072208220922102211221222132214221522162217221822192220222122222223222422252226222722282229223022312232223322342235223622372238223922402241224222432244224522462247224822492250225122522253225422552256225722582259226022612262226322642265226622672268226922702271227222732274227522762277227822792280228122822283228422852286228722882289229022912292229322942295229622972298229923002301230223032304230523062307230823092310231123122313231423152316231723182319232023212322232323242325232623272328232923302331233223332334233523362337233823392340234123422343234423452346234723482349235023512352235323542355235623572358235923602361236223632364236523662367236823692370237123722373237423752376237723782379238023812382238323842385238623872388238923902391239223932394239523962397239823992400240124022403240424052406240724082409241024112412241324142415241624172418241924202421242224232424242524262427242824292430243124322433243424352436243724382439244024412442244324442445244624472448244924502451245224532454245524562457245824592460246124622463246424652466246724682469247024712472247324742475247624772478247924802481248224832484248524862487248824892490249124922493249424952496249724982499250025012502250325042505250625072508250925102511251225132514251525162517251825192520252125222523
  1. /*
  2. * GTK UI
  3. *
  4. * Copyright IBM, Corp. 2012
  5. *
  6. * Authors:
  7. * Anthony Liguori <aliguori@us.ibm.com>
  8. *
  9. * This work is licensed under the terms of the GNU GPL, version 2 or later.
  10. * See the COPYING file in the top-level directory.
  11. *
  12. * Portions from gtk-vnc:
  13. *
  14. * GTK VNC Widget
  15. *
  16. * Copyright (C) 2006 Anthony Liguori <anthony@codemonkey.ws>
  17. * Copyright (C) 2009-2010 Daniel P. Berrange <dan@berrange.com>
  18. *
  19. * This library is free software; you can redistribute it and/or
  20. * modify it under the terms of the GNU Lesser General Public
  21. * License as published by the Free Software Foundation; either
  22. * version 2.0 of the License, or (at your option) any later version.
  23. *
  24. * This library is distributed in the hope that it will be useful,
  25. * but WITHOUT ANY WARRANTY; without even the implied warranty of
  26. * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
  27. * Lesser General Public License for more details.
  28. *
  29. * You should have received a copy of the GNU Lesser General Public
  30. * License along with this library; if not, write to the Free Software
  31. * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
  32. */
  33. #define GETTEXT_PACKAGE "qemu"
  34. #define LOCALEDIR "po"
  35. #include "qemu/osdep.h"
  36. #include "qemu-common.h"
  37. #include "qapi/error.h"
  38. #include "qapi/qapi-commands-misc.h"
  39. #include "qemu/cutils.h"
  40. #include "ui/console.h"
  41. #include "ui/gtk.h"
  42. #include <glib/gi18n.h>
  43. #include <locale.h>
  44. #if defined(CONFIG_VTE)
  45. #include <vte/vte.h>
  46. #endif
  47. #include <math.h>
  48. #include "trace.h"
  49. #include "ui/input.h"
  50. #include "sysemu/sysemu.h"
  51. #include "keymaps.h"
  52. #include "chardev/char.h"
  53. #include "qom/object.h"
  54. #define MAX_VCS 10
  55. #define VC_WINDOW_X_MIN 320
  56. #define VC_WINDOW_Y_MIN 240
  57. #define VC_TERM_X_MIN 80
  58. #define VC_TERM_Y_MIN 25
  59. #define VC_SCALE_MIN 0.25
  60. #define VC_SCALE_STEP 0.25
  61. #ifdef GDK_WINDOWING_X11
  62. #include "x_keymap.h"
  63. /* Gtk2 compat */
  64. #ifndef GDK_IS_X11_DISPLAY
  65. #define GDK_IS_X11_DISPLAY(dpy) (dpy != NULL)
  66. #endif
  67. #endif
  68. #ifdef GDK_WINDOWING_WAYLAND
  69. /* Gtk2 compat */
  70. #ifndef GDK_IS_WAYLAND_DISPLAY
  71. #define GDK_IS_WAYLAND_DISPLAY(dpy) (dpy != NULL)
  72. #endif
  73. #endif
  74. #ifdef GDK_WINDOWING_WIN32
  75. /* Gtk2 compat */
  76. #ifndef GDK_IS_WIN32_DISPLAY
  77. #define GDK_IS_WIN32_DISPLAY(dpy) (dpy != NULL)
  78. #endif
  79. #endif
  80. #ifdef GDK_WINDOWING_BROADWAY
  81. /* Gtk2 compat */
  82. #ifndef GDK_IS_BROADWAY_DISPLAY
  83. #define GDK_IS_BROADWAY_DISPLAY(dpy) (dpy != NULL)
  84. #endif
  85. #endif
  86. #ifdef GDK_WINDOWING_QUARTZ
  87. /* Gtk2 compat */
  88. #ifndef GDK_IS_QUARTZ_DISPLAY
  89. #define GDK_IS_QUARTZ_DISPLAY(dpy) (dpy != NULL)
  90. #endif
  91. #endif
  92. #if !defined(CONFIG_VTE)
  93. # define VTE_CHECK_VERSION(a, b, c) 0
  94. #endif
  95. #if defined(CONFIG_VTE) && !GTK_CHECK_VERSION(3, 0, 0)
  96. /*
  97. * The gtk2 vte terminal widget seriously messes up the window resize
  98. * for some reason. You basically can't make the qemu window smaller
  99. * any more because the toplevel window geoemtry hints are overridden.
  100. *
  101. * Workaround that by hiding all vte widgets, except the one in the
  102. * current tab.
  103. *
  104. * Luckily everything works smooth in gtk3.
  105. */
  106. # define VTE_RESIZE_HACK 1
  107. #endif
  108. #if !GTK_CHECK_VERSION(2, 20, 0)
  109. #define gtk_widget_get_realized(widget) GTK_WIDGET_REALIZED(widget)
  110. #endif
  111. #ifndef GDK_IS_X11_DISPLAY
  112. #define GDK_IS_X11_DISPLAY(dpy) (dpy == dpy)
  113. #endif
  114. #ifndef GDK_IS_WAYLAND_DISPLAY
  115. #define GDK_IS_WAYLAND_DISPLAY(dpy) (dpy == dpy)
  116. #endif
  117. #ifndef GDK_IS_WIN32_DISPLAY
  118. #define GDK_IS_WIN32_DISPLAY(dpy) (dpy == dpy)
  119. #endif
  120. #if !GTK_CHECK_VERSION(2, 22, 0)
  121. #define GDK_KEY_0 GDK_0
  122. #define GDK_KEY_1 GDK_1
  123. #define GDK_KEY_2 GDK_2
  124. #define GDK_KEY_f GDK_f
  125. #define GDK_KEY_g GDK_g
  126. #define GDK_KEY_m GDK_m
  127. #define GDK_KEY_q GDK_q
  128. #define GDK_KEY_plus GDK_plus
  129. #define GDK_KEY_equal GDK_equal
  130. #define GDK_KEY_minus GDK_minus
  131. #define GDK_KEY_Pause GDK_Pause
  132. #define GDK_KEY_Delete GDK_Delete
  133. #endif
  134. /* Some older mingw versions lack this constant or have
  135. * it conditionally defined */
  136. #ifdef _WIN32
  137. # ifndef MAPVK_VK_TO_VSC
  138. # define MAPVK_VK_TO_VSC 0
  139. # endif
  140. #endif
  141. #define HOTKEY_MODIFIERS (GDK_CONTROL_MASK | GDK_MOD1_MASK)
  142. static const int modifier_keycode[] = {
  143. Q_KEY_CODE_SHIFT,
  144. Q_KEY_CODE_SHIFT_R,
  145. Q_KEY_CODE_CTRL,
  146. Q_KEY_CODE_CTRL_R,
  147. Q_KEY_CODE_ALT,
  148. Q_KEY_CODE_ALT_R,
  149. Q_KEY_CODE_META_L,
  150. Q_KEY_CODE_META_R,
  151. };
  152. static const guint16 *keycode_map;
  153. static size_t keycode_maplen;
  154. struct GtkDisplayState {
  155. GtkWidget *window;
  156. GtkWidget *menu_bar;
  157. GtkAccelGroup *accel_group;
  158. GtkWidget *machine_menu_item;
  159. GtkWidget *machine_menu;
  160. GtkWidget *pause_item;
  161. GtkWidget *reset_item;
  162. GtkWidget *powerdown_item;
  163. GtkWidget *quit_item;
  164. GtkWidget *view_menu_item;
  165. GtkWidget *view_menu;
  166. GtkWidget *full_screen_item;
  167. GtkWidget *copy_item;
  168. GtkWidget *zoom_in_item;
  169. GtkWidget *zoom_out_item;
  170. GtkWidget *zoom_fixed_item;
  171. GtkWidget *zoom_fit_item;
  172. GtkWidget *grab_item;
  173. GtkWidget *grab_on_hover_item;
  174. int nb_vcs;
  175. VirtualConsole vc[MAX_VCS];
  176. GtkWidget *show_tabs_item;
  177. GtkWidget *untabify_item;
  178. GtkWidget *show_menubar_item;
  179. GtkWidget *vbox;
  180. GtkWidget *notebook;
  181. int button_mask;
  182. gboolean last_set;
  183. int last_x;
  184. int last_y;
  185. int grab_x_root;
  186. int grab_y_root;
  187. VirtualConsole *kbd_owner;
  188. VirtualConsole *ptr_owner;
  189. gboolean full_screen;
  190. GdkCursor *null_cursor;
  191. Notifier mouse_mode_notifier;
  192. gboolean free_scale;
  193. bool external_pause_update;
  194. bool modifier_pressed[ARRAY_SIZE(modifier_keycode)];
  195. bool ignore_keys;
  196. DisplayOptions *opts;
  197. };
  198. typedef struct VCChardev {
  199. Chardev parent;
  200. VirtualConsole *console;
  201. bool echo;
  202. } VCChardev;
  203. #define TYPE_CHARDEV_VC "chardev-vc"
  204. #define VC_CHARDEV(obj) OBJECT_CHECK(VCChardev, (obj), TYPE_CHARDEV_VC)
  205. bool gtk_use_gl_area;
  206. static void gd_grab_pointer(VirtualConsole *vc, const char *reason);
  207. static void gd_ungrab_pointer(GtkDisplayState *s);
  208. static void gd_grab_keyboard(VirtualConsole *vc, const char *reason);
  209. static void gd_ungrab_keyboard(GtkDisplayState *s);
  210. /** Utility Functions **/
  211. static VirtualConsole *gd_vc_find_by_menu(GtkDisplayState *s)
  212. {
  213. VirtualConsole *vc;
  214. gint i;
  215. for (i = 0; i < s->nb_vcs; i++) {
  216. vc = &s->vc[i];
  217. if (gtk_check_menu_item_get_active
  218. (GTK_CHECK_MENU_ITEM(vc->menu_item))) {
  219. return vc;
  220. }
  221. }
  222. return NULL;
  223. }
  224. static VirtualConsole *gd_vc_find_by_page(GtkDisplayState *s, gint page)
  225. {
  226. VirtualConsole *vc;
  227. gint i, p;
  228. for (i = 0; i < s->nb_vcs; i++) {
  229. vc = &s->vc[i];
  230. p = gtk_notebook_page_num(GTK_NOTEBOOK(s->notebook), vc->tab_item);
  231. if (p == page) {
  232. return vc;
  233. }
  234. }
  235. return NULL;
  236. }
  237. static VirtualConsole *gd_vc_find_current(GtkDisplayState *s)
  238. {
  239. gint page;
  240. page = gtk_notebook_get_current_page(GTK_NOTEBOOK(s->notebook));
  241. return gd_vc_find_by_page(s, page);
  242. }
  243. static bool gd_is_grab_active(GtkDisplayState *s)
  244. {
  245. return gtk_check_menu_item_get_active(GTK_CHECK_MENU_ITEM(s->grab_item));
  246. }
  247. static bool gd_grab_on_hover(GtkDisplayState *s)
  248. {
  249. return gtk_check_menu_item_get_active(GTK_CHECK_MENU_ITEM(s->grab_on_hover_item));
  250. }
  251. static void gd_update_cursor(VirtualConsole *vc)
  252. {
  253. GtkDisplayState *s = vc->s;
  254. GdkWindow *window;
  255. if (vc->type != GD_VC_GFX ||
  256. !qemu_console_is_graphic(vc->gfx.dcl.con)) {
  257. return;
  258. }
  259. if (!gtk_widget_get_realized(vc->gfx.drawing_area)) {
  260. return;
  261. }
  262. window = gtk_widget_get_window(GTK_WIDGET(vc->gfx.drawing_area));
  263. if (s->full_screen || qemu_input_is_absolute() || s->ptr_owner == vc) {
  264. gdk_window_set_cursor(window, s->null_cursor);
  265. } else {
  266. gdk_window_set_cursor(window, NULL);
  267. }
  268. }
  269. static void gd_update_caption(GtkDisplayState *s)
  270. {
  271. const char *status = "";
  272. gchar *prefix;
  273. gchar *title;
  274. const char *grab = "";
  275. bool is_paused = !runstate_is_running();
  276. int i;
  277. if (qemu_name) {
  278. prefix = g_strdup_printf("QEMU (%s)", qemu_name);
  279. } else {
  280. prefix = g_strdup_printf("QEMU");
  281. }
  282. if (s->ptr_owner != NULL &&
  283. s->ptr_owner->window == NULL) {
  284. grab = _(" - Press Ctrl+Alt+G to release grab");
  285. }
  286. if (is_paused) {
  287. status = _(" [Paused]");
  288. }
  289. s->external_pause_update = true;
  290. gtk_check_menu_item_set_active(GTK_CHECK_MENU_ITEM(s->pause_item),
  291. is_paused);
  292. s->external_pause_update = false;
  293. title = g_strdup_printf("%s%s%s", prefix, status, grab);
  294. gtk_window_set_title(GTK_WINDOW(s->window), title);
  295. g_free(title);
  296. for (i = 0; i < s->nb_vcs; i++) {
  297. VirtualConsole *vc = &s->vc[i];
  298. if (!vc->window) {
  299. continue;
  300. }
  301. title = g_strdup_printf("%s: %s%s%s", prefix, vc->label,
  302. vc == s->kbd_owner ? " +kbd" : "",
  303. vc == s->ptr_owner ? " +ptr" : "");
  304. gtk_window_set_title(GTK_WINDOW(vc->window), title);
  305. g_free(title);
  306. }
  307. g_free(prefix);
  308. }
  309. static void gd_update_geometry_hints(VirtualConsole *vc)
  310. {
  311. GtkDisplayState *s = vc->s;
  312. GdkWindowHints mask = 0;
  313. GdkGeometry geo = {};
  314. GtkWidget *geo_widget = NULL;
  315. GtkWindow *geo_window;
  316. if (vc->type == GD_VC_GFX) {
  317. if (!vc->gfx.ds) {
  318. return;
  319. }
  320. if (s->free_scale) {
  321. geo.min_width = surface_width(vc->gfx.ds) * VC_SCALE_MIN;
  322. geo.min_height = surface_height(vc->gfx.ds) * VC_SCALE_MIN;
  323. mask |= GDK_HINT_MIN_SIZE;
  324. } else {
  325. geo.min_width = surface_width(vc->gfx.ds) * vc->gfx.scale_x;
  326. geo.min_height = surface_height(vc->gfx.ds) * vc->gfx.scale_y;
  327. mask |= GDK_HINT_MIN_SIZE;
  328. }
  329. geo_widget = vc->gfx.drawing_area;
  330. gtk_widget_set_size_request(geo_widget, geo.min_width, geo.min_height);
  331. #if defined(CONFIG_VTE)
  332. } else if (vc->type == GD_VC_VTE) {
  333. VteTerminal *term = VTE_TERMINAL(vc->vte.terminal);
  334. GtkBorder padding = { 0 };
  335. #if VTE_CHECK_VERSION(0, 37, 0)
  336. gtk_style_context_get_padding(
  337. gtk_widget_get_style_context(vc->vte.terminal),
  338. gtk_widget_get_state_flags(vc->vte.terminal),
  339. &padding);
  340. #else
  341. {
  342. GtkBorder *ib = NULL;
  343. gtk_widget_style_get(vc->vte.terminal, "inner-border", &ib, NULL);
  344. if (ib) {
  345. padding = *ib;
  346. gtk_border_free(ib);
  347. }
  348. }
  349. #endif
  350. geo.width_inc = vte_terminal_get_char_width(term);
  351. geo.height_inc = vte_terminal_get_char_height(term);
  352. mask |= GDK_HINT_RESIZE_INC;
  353. geo.base_width = geo.width_inc;
  354. geo.base_height = geo.height_inc;
  355. mask |= GDK_HINT_BASE_SIZE;
  356. geo.min_width = geo.width_inc * VC_TERM_X_MIN;
  357. geo.min_height = geo.height_inc * VC_TERM_Y_MIN;
  358. mask |= GDK_HINT_MIN_SIZE;
  359. geo.base_width += padding.left + padding.right;
  360. geo.base_height += padding.top + padding.bottom;
  361. geo.min_width += padding.left + padding.right;
  362. geo.min_height += padding.top + padding.bottom;
  363. geo_widget = vc->vte.terminal;
  364. #endif
  365. }
  366. geo_window = GTK_WINDOW(vc->window ? vc->window : s->window);
  367. gtk_window_set_geometry_hints(geo_window, geo_widget, &geo, mask);
  368. }
  369. void gd_update_windowsize(VirtualConsole *vc)
  370. {
  371. GtkDisplayState *s = vc->s;
  372. gd_update_geometry_hints(vc);
  373. if (vc->type == GD_VC_GFX && !s->full_screen && !s->free_scale) {
  374. gtk_window_resize(GTK_WINDOW(vc->window ? vc->window : s->window),
  375. VC_WINDOW_X_MIN, VC_WINDOW_Y_MIN);
  376. }
  377. }
  378. static void gd_update_full_redraw(VirtualConsole *vc)
  379. {
  380. GtkWidget *area = vc->gfx.drawing_area;
  381. int ww, wh;
  382. gdk_drawable_get_size(gtk_widget_get_window(area), &ww, &wh);
  383. #if defined(CONFIG_GTK_GL)
  384. if (vc->gfx.gls && gtk_use_gl_area) {
  385. gtk_gl_area_queue_render(GTK_GL_AREA(vc->gfx.drawing_area));
  386. return;
  387. }
  388. #endif
  389. gtk_widget_queue_draw_area(area, 0, 0, ww, wh);
  390. }
  391. static void gtk_release_modifiers(GtkDisplayState *s)
  392. {
  393. VirtualConsole *vc = gd_vc_find_current(s);
  394. int i, qcode;
  395. if (vc->type != GD_VC_GFX ||
  396. !qemu_console_is_graphic(vc->gfx.dcl.con)) {
  397. return;
  398. }
  399. for (i = 0; i < ARRAY_SIZE(modifier_keycode); i++) {
  400. qcode = modifier_keycode[i];
  401. if (!s->modifier_pressed[i]) {
  402. continue;
  403. }
  404. qemu_input_event_send_key_qcode(vc->gfx.dcl.con, qcode, false);
  405. s->modifier_pressed[i] = false;
  406. }
  407. }
  408. static void gd_widget_reparent(GtkWidget *from, GtkWidget *to,
  409. GtkWidget *widget)
  410. {
  411. g_object_ref(G_OBJECT(widget));
  412. gtk_container_remove(GTK_CONTAINER(from), widget);
  413. gtk_container_add(GTK_CONTAINER(to), widget);
  414. g_object_unref(G_OBJECT(widget));
  415. }
  416. /** DisplayState Callbacks **/
  417. static void gd_update(DisplayChangeListener *dcl,
  418. int x, int y, int w, int h)
  419. {
  420. VirtualConsole *vc = container_of(dcl, VirtualConsole, gfx.dcl);
  421. GdkWindow *win;
  422. int x1, x2, y1, y2;
  423. int mx, my;
  424. int fbw, fbh;
  425. int ww, wh;
  426. trace_gd_update(vc->label, x, y, w, h);
  427. if (!gtk_widget_get_realized(vc->gfx.drawing_area)) {
  428. return;
  429. }
  430. if (vc->gfx.convert) {
  431. pixman_image_composite(PIXMAN_OP_SRC, vc->gfx.ds->image,
  432. NULL, vc->gfx.convert,
  433. x, y, 0, 0, x, y, w, h);
  434. }
  435. x1 = floor(x * vc->gfx.scale_x);
  436. y1 = floor(y * vc->gfx.scale_y);
  437. x2 = ceil(x * vc->gfx.scale_x + w * vc->gfx.scale_x);
  438. y2 = ceil(y * vc->gfx.scale_y + h * vc->gfx.scale_y);
  439. fbw = surface_width(vc->gfx.ds) * vc->gfx.scale_x;
  440. fbh = surface_height(vc->gfx.ds) * vc->gfx.scale_y;
  441. win = gtk_widget_get_window(vc->gfx.drawing_area);
  442. if (!win) {
  443. return;
  444. }
  445. gdk_drawable_get_size(win, &ww, &wh);
  446. mx = my = 0;
  447. if (ww > fbw) {
  448. mx = (ww - fbw) / 2;
  449. }
  450. if (wh > fbh) {
  451. my = (wh - fbh) / 2;
  452. }
  453. gtk_widget_queue_draw_area(vc->gfx.drawing_area,
  454. mx + x1, my + y1, (x2 - x1), (y2 - y1));
  455. }
  456. static void gd_refresh(DisplayChangeListener *dcl)
  457. {
  458. graphic_hw_update(dcl->con);
  459. }
  460. #if GTK_CHECK_VERSION(3, 0, 0)
  461. static GdkDevice *gd_get_pointer(GdkDisplay *dpy)
  462. {
  463. #if GTK_CHECK_VERSION(3, 20, 0)
  464. return gdk_seat_get_pointer(gdk_display_get_default_seat(dpy));
  465. #else
  466. return gdk_device_manager_get_client_pointer(
  467. gdk_display_get_device_manager(dpy));
  468. #endif
  469. }
  470. static void gd_mouse_set(DisplayChangeListener *dcl,
  471. int x, int y, int visible)
  472. {
  473. VirtualConsole *vc = container_of(dcl, VirtualConsole, gfx.dcl);
  474. GdkDisplay *dpy;
  475. gint x_root, y_root;
  476. if (qemu_input_is_absolute()) {
  477. return;
  478. }
  479. dpy = gtk_widget_get_display(vc->gfx.drawing_area);
  480. gdk_window_get_root_coords(gtk_widget_get_window(vc->gfx.drawing_area),
  481. x, y, &x_root, &y_root);
  482. gdk_device_warp(gd_get_pointer(dpy),
  483. gtk_widget_get_screen(vc->gfx.drawing_area),
  484. x_root, y_root);
  485. vc->s->last_x = x;
  486. vc->s->last_y = y;
  487. }
  488. #else
  489. static void gd_mouse_set(DisplayChangeListener *dcl,
  490. int x, int y, int visible)
  491. {
  492. VirtualConsole *vc = container_of(dcl, VirtualConsole, gfx.dcl);
  493. gint x_root, y_root;
  494. if (qemu_input_is_absolute()) {
  495. return;
  496. }
  497. gdk_window_get_root_coords(gtk_widget_get_window(vc->gfx.drawing_area),
  498. x, y, &x_root, &y_root);
  499. gdk_display_warp_pointer(gtk_widget_get_display(vc->gfx.drawing_area),
  500. gtk_widget_get_screen(vc->gfx.drawing_area),
  501. x_root, y_root);
  502. }
  503. #endif
  504. static void gd_cursor_define(DisplayChangeListener *dcl,
  505. QEMUCursor *c)
  506. {
  507. VirtualConsole *vc = container_of(dcl, VirtualConsole, gfx.dcl);
  508. GdkPixbuf *pixbuf;
  509. GdkCursor *cursor;
  510. if (!gtk_widget_get_realized(vc->gfx.drawing_area)) {
  511. return;
  512. }
  513. pixbuf = gdk_pixbuf_new_from_data((guchar *)(c->data),
  514. GDK_COLORSPACE_RGB, true, 8,
  515. c->width, c->height, c->width * 4,
  516. NULL, NULL);
  517. cursor = gdk_cursor_new_from_pixbuf
  518. (gtk_widget_get_display(vc->gfx.drawing_area),
  519. pixbuf, c->hot_x, c->hot_y);
  520. gdk_window_set_cursor(gtk_widget_get_window(vc->gfx.drawing_area), cursor);
  521. g_object_unref(pixbuf);
  522. #if !GTK_CHECK_VERSION(3, 0, 0)
  523. gdk_cursor_unref(cursor);
  524. #else
  525. g_object_unref(cursor);
  526. #endif
  527. }
  528. static void gd_switch(DisplayChangeListener *dcl,
  529. DisplaySurface *surface)
  530. {
  531. VirtualConsole *vc = container_of(dcl, VirtualConsole, gfx.dcl);
  532. bool resized = true;
  533. trace_gd_switch(vc->label,
  534. surface ? surface_width(surface) : 0,
  535. surface ? surface_height(surface) : 0);
  536. if (vc->gfx.surface) {
  537. cairo_surface_destroy(vc->gfx.surface);
  538. vc->gfx.surface = NULL;
  539. }
  540. if (vc->gfx.convert) {
  541. pixman_image_unref(vc->gfx.convert);
  542. vc->gfx.convert = NULL;
  543. }
  544. if (vc->gfx.ds && surface &&
  545. surface_width(vc->gfx.ds) == surface_width(surface) &&
  546. surface_height(vc->gfx.ds) == surface_height(surface)) {
  547. resized = false;
  548. }
  549. vc->gfx.ds = surface;
  550. if (!surface) {
  551. return;
  552. }
  553. if (surface->format == PIXMAN_x8r8g8b8) {
  554. /*
  555. * PIXMAN_x8r8g8b8 == CAIRO_FORMAT_RGB24
  556. *
  557. * No need to convert, use surface directly. Should be the
  558. * common case as this is qemu_default_pixelformat(32) too.
  559. */
  560. vc->gfx.surface = cairo_image_surface_create_for_data
  561. (surface_data(surface),
  562. CAIRO_FORMAT_RGB24,
  563. surface_width(surface),
  564. surface_height(surface),
  565. surface_stride(surface));
  566. } else {
  567. /* Must convert surface, use pixman to do it. */
  568. vc->gfx.convert = pixman_image_create_bits(PIXMAN_x8r8g8b8,
  569. surface_width(surface),
  570. surface_height(surface),
  571. NULL, 0);
  572. vc->gfx.surface = cairo_image_surface_create_for_data
  573. ((void *)pixman_image_get_data(vc->gfx.convert),
  574. CAIRO_FORMAT_RGB24,
  575. pixman_image_get_width(vc->gfx.convert),
  576. pixman_image_get_height(vc->gfx.convert),
  577. pixman_image_get_stride(vc->gfx.convert));
  578. pixman_image_composite(PIXMAN_OP_SRC, vc->gfx.ds->image,
  579. NULL, vc->gfx.convert,
  580. 0, 0, 0, 0, 0, 0,
  581. pixman_image_get_width(vc->gfx.convert),
  582. pixman_image_get_height(vc->gfx.convert));
  583. }
  584. if (resized) {
  585. gd_update_windowsize(vc);
  586. } else {
  587. gd_update_full_redraw(vc);
  588. }
  589. }
  590. static const DisplayChangeListenerOps dcl_ops = {
  591. .dpy_name = "gtk",
  592. .dpy_gfx_update = gd_update,
  593. .dpy_gfx_switch = gd_switch,
  594. .dpy_gfx_check_format = qemu_pixman_check_format,
  595. .dpy_refresh = gd_refresh,
  596. .dpy_mouse_set = gd_mouse_set,
  597. .dpy_cursor_define = gd_cursor_define,
  598. };
  599. #if defined(CONFIG_OPENGL)
  600. /** DisplayState Callbacks (opengl version) **/
  601. #if defined(CONFIG_GTK_GL)
  602. static const DisplayChangeListenerOps dcl_gl_area_ops = {
  603. .dpy_name = "gtk-egl",
  604. .dpy_gfx_update = gd_gl_area_update,
  605. .dpy_gfx_switch = gd_gl_area_switch,
  606. .dpy_gfx_check_format = console_gl_check_format,
  607. .dpy_refresh = gd_gl_area_refresh,
  608. .dpy_mouse_set = gd_mouse_set,
  609. .dpy_cursor_define = gd_cursor_define,
  610. .dpy_gl_ctx_create = gd_gl_area_create_context,
  611. .dpy_gl_ctx_destroy = gd_gl_area_destroy_context,
  612. .dpy_gl_ctx_make_current = gd_gl_area_make_current,
  613. .dpy_gl_ctx_get_current = gd_gl_area_get_current_context,
  614. .dpy_gl_scanout_texture = gd_gl_area_scanout_texture,
  615. .dpy_gl_update = gd_gl_area_scanout_flush,
  616. };
  617. #endif /* CONFIG_GTK_GL */
  618. static const DisplayChangeListenerOps dcl_egl_ops = {
  619. .dpy_name = "gtk-egl",
  620. .dpy_gfx_update = gd_egl_update,
  621. .dpy_gfx_switch = gd_egl_switch,
  622. .dpy_gfx_check_format = console_gl_check_format,
  623. .dpy_refresh = gd_egl_refresh,
  624. .dpy_mouse_set = gd_mouse_set,
  625. .dpy_cursor_define = gd_cursor_define,
  626. .dpy_gl_ctx_create = gd_egl_create_context,
  627. .dpy_gl_ctx_destroy = qemu_egl_destroy_context,
  628. .dpy_gl_ctx_make_current = gd_egl_make_current,
  629. .dpy_gl_ctx_get_current = qemu_egl_get_current_context,
  630. .dpy_gl_scanout_disable = gd_egl_scanout_disable,
  631. .dpy_gl_scanout_texture = gd_egl_scanout_texture,
  632. .dpy_gl_scanout_dmabuf = gd_egl_scanout_dmabuf,
  633. .dpy_gl_cursor_dmabuf = gd_egl_cursor_dmabuf,
  634. .dpy_gl_cursor_position = gd_egl_cursor_position,
  635. .dpy_gl_release_dmabuf = gd_egl_release_dmabuf,
  636. .dpy_gl_update = gd_egl_scanout_flush,
  637. };
  638. #endif /* CONFIG_OPENGL */
  639. /** QEMU Events **/
  640. static void gd_change_runstate(void *opaque, int running, RunState state)
  641. {
  642. GtkDisplayState *s = opaque;
  643. gd_update_caption(s);
  644. }
  645. static void gd_mouse_mode_change(Notifier *notify, void *data)
  646. {
  647. GtkDisplayState *s;
  648. int i;
  649. s = container_of(notify, GtkDisplayState, mouse_mode_notifier);
  650. /* release the grab at switching to absolute mode */
  651. if (qemu_input_is_absolute() && gd_is_grab_active(s)) {
  652. gtk_check_menu_item_set_active(GTK_CHECK_MENU_ITEM(s->grab_item),
  653. FALSE);
  654. }
  655. for (i = 0; i < s->nb_vcs; i++) {
  656. VirtualConsole *vc = &s->vc[i];
  657. gd_update_cursor(vc);
  658. }
  659. }
  660. /** GTK Events **/
  661. static gboolean gd_window_close(GtkWidget *widget, GdkEvent *event,
  662. void *opaque)
  663. {
  664. GtkDisplayState *s = opaque;
  665. bool allow_close = true;
  666. if (s->opts->has_window_close && !s->opts->window_close) {
  667. allow_close = false;
  668. }
  669. if (allow_close) {
  670. qmp_quit(NULL);
  671. }
  672. return TRUE;
  673. }
  674. static void gd_set_ui_info(VirtualConsole *vc, gint width, gint height)
  675. {
  676. QemuUIInfo info;
  677. memset(&info, 0, sizeof(info));
  678. info.width = width;
  679. info.height = height;
  680. dpy_set_ui_info(vc->gfx.dcl.con, &info);
  681. }
  682. #if defined(CONFIG_GTK_GL)
  683. static gboolean gd_render_event(GtkGLArea *area, GdkGLContext *context,
  684. void *opaque)
  685. {
  686. VirtualConsole *vc = opaque;
  687. if (vc->gfx.gls) {
  688. gd_gl_area_draw(vc);
  689. }
  690. return TRUE;
  691. }
  692. static void gd_resize_event(GtkGLArea *area,
  693. gint width, gint height, gpointer *opaque)
  694. {
  695. VirtualConsole *vc = (void *)opaque;
  696. gd_set_ui_info(vc, width, height);
  697. }
  698. #endif
  699. static gboolean gd_draw_event(GtkWidget *widget, cairo_t *cr, void *opaque)
  700. {
  701. VirtualConsole *vc = opaque;
  702. GtkDisplayState *s = vc->s;
  703. int mx, my;
  704. int ww, wh;
  705. int fbw, fbh;
  706. #if defined(CONFIG_OPENGL)
  707. if (vc->gfx.gls) {
  708. if (gtk_use_gl_area) {
  709. /* invoke render callback please */
  710. return FALSE;
  711. } else {
  712. gd_egl_draw(vc);
  713. return TRUE;
  714. }
  715. }
  716. #endif
  717. if (!gtk_widget_get_realized(widget)) {
  718. return FALSE;
  719. }
  720. if (!vc->gfx.ds) {
  721. return FALSE;
  722. }
  723. fbw = surface_width(vc->gfx.ds);
  724. fbh = surface_height(vc->gfx.ds);
  725. gdk_drawable_get_size(gtk_widget_get_window(widget), &ww, &wh);
  726. if (s->full_screen) {
  727. vc->gfx.scale_x = (double)ww / fbw;
  728. vc->gfx.scale_y = (double)wh / fbh;
  729. } else if (s->free_scale) {
  730. double sx, sy;
  731. sx = (double)ww / fbw;
  732. sy = (double)wh / fbh;
  733. vc->gfx.scale_x = vc->gfx.scale_y = MIN(sx, sy);
  734. }
  735. fbw *= vc->gfx.scale_x;
  736. fbh *= vc->gfx.scale_y;
  737. mx = my = 0;
  738. if (ww > fbw) {
  739. mx = (ww - fbw) / 2;
  740. }
  741. if (wh > fbh) {
  742. my = (wh - fbh) / 2;
  743. }
  744. cairo_rectangle(cr, 0, 0, ww, wh);
  745. /* Optionally cut out the inner area where the pixmap
  746. will be drawn. This avoids 'flashing' since we're
  747. not double-buffering. Note we're using the undocumented
  748. behaviour of drawing the rectangle from right to left
  749. to cut out the whole */
  750. cairo_rectangle(cr, mx + fbw, my,
  751. -1 * fbw, fbh);
  752. cairo_fill(cr);
  753. cairo_scale(cr, vc->gfx.scale_x, vc->gfx.scale_y);
  754. cairo_set_source_surface(cr, vc->gfx.surface,
  755. mx / vc->gfx.scale_x, my / vc->gfx.scale_y);
  756. cairo_paint(cr);
  757. return TRUE;
  758. }
  759. #if !GTK_CHECK_VERSION(3, 0, 0)
  760. static gboolean gd_expose_event(GtkWidget *widget, GdkEventExpose *expose,
  761. void *opaque)
  762. {
  763. cairo_t *cr;
  764. gboolean ret;
  765. cr = gdk_cairo_create(gtk_widget_get_window(widget));
  766. cairo_rectangle(cr,
  767. expose->area.x,
  768. expose->area.y,
  769. expose->area.width,
  770. expose->area.height);
  771. cairo_clip(cr);
  772. ret = gd_draw_event(widget, cr, opaque);
  773. cairo_destroy(cr);
  774. return ret;
  775. }
  776. #endif
  777. static gboolean gd_motion_event(GtkWidget *widget, GdkEventMotion *motion,
  778. void *opaque)
  779. {
  780. VirtualConsole *vc = opaque;
  781. GtkDisplayState *s = vc->s;
  782. int x, y;
  783. int mx, my;
  784. int fbh, fbw;
  785. int ww, wh;
  786. if (!vc->gfx.ds) {
  787. return TRUE;
  788. }
  789. fbw = surface_width(vc->gfx.ds) * vc->gfx.scale_x;
  790. fbh = surface_height(vc->gfx.ds) * vc->gfx.scale_y;
  791. gdk_drawable_get_size(gtk_widget_get_window(vc->gfx.drawing_area),
  792. &ww, &wh);
  793. mx = my = 0;
  794. if (ww > fbw) {
  795. mx = (ww - fbw) / 2;
  796. }
  797. if (wh > fbh) {
  798. my = (wh - fbh) / 2;
  799. }
  800. x = (motion->x - mx) / vc->gfx.scale_x;
  801. y = (motion->y - my) / vc->gfx.scale_y;
  802. if (qemu_input_is_absolute()) {
  803. if (x < 0 || y < 0 ||
  804. x >= surface_width(vc->gfx.ds) ||
  805. y >= surface_height(vc->gfx.ds)) {
  806. return TRUE;
  807. }
  808. qemu_input_queue_abs(vc->gfx.dcl.con, INPUT_AXIS_X, x,
  809. 0, surface_width(vc->gfx.ds));
  810. qemu_input_queue_abs(vc->gfx.dcl.con, INPUT_AXIS_Y, y,
  811. 0, surface_height(vc->gfx.ds));
  812. qemu_input_event_sync();
  813. } else if (s->last_set && s->ptr_owner == vc) {
  814. qemu_input_queue_rel(vc->gfx.dcl.con, INPUT_AXIS_X, x - s->last_x);
  815. qemu_input_queue_rel(vc->gfx.dcl.con, INPUT_AXIS_Y, y - s->last_y);
  816. qemu_input_event_sync();
  817. }
  818. s->last_x = x;
  819. s->last_y = y;
  820. s->last_set = TRUE;
  821. if (!qemu_input_is_absolute() && s->ptr_owner == vc) {
  822. GdkScreen *screen = gtk_widget_get_screen(vc->gfx.drawing_area);
  823. int screen_width, screen_height;
  824. int x = (int)motion->x_root;
  825. int y = (int)motion->y_root;
  826. #if GTK_CHECK_VERSION(3, 22, 0)
  827. {
  828. GdkDisplay *dpy = gtk_widget_get_display(widget);
  829. GdkWindow *win = gtk_widget_get_window(widget);
  830. GdkMonitor *monitor = gdk_display_get_monitor_at_window(dpy, win);
  831. GdkRectangle geometry;
  832. gdk_monitor_get_geometry(monitor, &geometry);
  833. screen_width = geometry.width;
  834. screen_height = geometry.height;
  835. }
  836. #else
  837. {
  838. screen_width = gdk_screen_get_width(screen);
  839. screen_height = gdk_screen_get_height(screen);
  840. }
  841. #endif
  842. /* In relative mode check to see if client pointer hit
  843. * one of the screen edges, and if so move it back by
  844. * 200 pixels. This is important because the pointer
  845. * in the server doesn't correspond 1-for-1, and so
  846. * may still be only half way across the screen. Without
  847. * this warp, the server pointer would thus appear to hit
  848. * an invisible wall */
  849. if (x == 0) {
  850. x += 200;
  851. }
  852. if (y == 0) {
  853. y += 200;
  854. }
  855. if (x == (screen_width - 1)) {
  856. x -= 200;
  857. }
  858. if (y == (screen_height - 1)) {
  859. y -= 200;
  860. }
  861. if (x != (int)motion->x_root || y != (int)motion->y_root) {
  862. #if GTK_CHECK_VERSION(3, 0, 0)
  863. GdkDevice *dev = gdk_event_get_device((GdkEvent *)motion);
  864. gdk_device_warp(dev, screen, x, y);
  865. #else
  866. GdkDisplay *display = gtk_widget_get_display(widget);
  867. gdk_display_warp_pointer(display, screen, x, y);
  868. #endif
  869. s->last_set = FALSE;
  870. return FALSE;
  871. }
  872. }
  873. return TRUE;
  874. }
  875. static gboolean gd_button_event(GtkWidget *widget, GdkEventButton *button,
  876. void *opaque)
  877. {
  878. VirtualConsole *vc = opaque;
  879. GtkDisplayState *s = vc->s;
  880. InputButton btn;
  881. /* implicitly grab the input at the first click in the relative mode */
  882. if (button->button == 1 && button->type == GDK_BUTTON_PRESS &&
  883. !qemu_input_is_absolute() && s->ptr_owner != vc) {
  884. if (!vc->window) {
  885. gtk_check_menu_item_set_active(GTK_CHECK_MENU_ITEM(s->grab_item),
  886. TRUE);
  887. } else {
  888. gd_grab_pointer(vc, "relative-mode-click");
  889. }
  890. return TRUE;
  891. }
  892. if (button->button == 1) {
  893. btn = INPUT_BUTTON_LEFT;
  894. } else if (button->button == 2) {
  895. btn = INPUT_BUTTON_MIDDLE;
  896. } else if (button->button == 3) {
  897. btn = INPUT_BUTTON_RIGHT;
  898. } else if (button->button == 8) {
  899. btn = INPUT_BUTTON_SIDE;
  900. } else if (button->button == 9) {
  901. btn = INPUT_BUTTON_EXTRA;
  902. } else {
  903. return TRUE;
  904. }
  905. qemu_input_queue_btn(vc->gfx.dcl.con, btn,
  906. button->type == GDK_BUTTON_PRESS);
  907. qemu_input_event_sync();
  908. return TRUE;
  909. }
  910. static gboolean gd_scroll_event(GtkWidget *widget, GdkEventScroll *scroll,
  911. void *opaque)
  912. {
  913. VirtualConsole *vc = opaque;
  914. InputButton btn;
  915. if (scroll->direction == GDK_SCROLL_UP) {
  916. btn = INPUT_BUTTON_WHEEL_UP;
  917. } else if (scroll->direction == GDK_SCROLL_DOWN) {
  918. btn = INPUT_BUTTON_WHEEL_DOWN;
  919. #if GTK_CHECK_VERSION(3, 4, 0)
  920. } else if (scroll->direction == GDK_SCROLL_SMOOTH) {
  921. gdouble delta_x, delta_y;
  922. if (!gdk_event_get_scroll_deltas((GdkEvent *)scroll,
  923. &delta_x, &delta_y)) {
  924. return TRUE;
  925. }
  926. if (delta_y > 0) {
  927. btn = INPUT_BUTTON_WHEEL_DOWN;
  928. } else {
  929. btn = INPUT_BUTTON_WHEEL_UP;
  930. }
  931. #endif
  932. } else {
  933. return TRUE;
  934. }
  935. qemu_input_queue_btn(vc->gfx.dcl.con, btn, true);
  936. qemu_input_event_sync();
  937. qemu_input_queue_btn(vc->gfx.dcl.con, btn, false);
  938. qemu_input_event_sync();
  939. return TRUE;
  940. }
  941. static const guint16 *gd_get_keymap(size_t *maplen)
  942. {
  943. GdkDisplay *dpy = gdk_display_get_default();
  944. #ifdef GDK_WINDOWING_X11
  945. if (GDK_IS_X11_DISPLAY(dpy)) {
  946. trace_gd_keymap_windowing("x11");
  947. return qemu_xkeymap_mapping_table(
  948. gdk_x11_display_get_xdisplay(dpy), maplen);
  949. }
  950. #endif
  951. #ifdef GDK_WINDOWING_WAYLAND
  952. if (GDK_IS_WAYLAND_DISPLAY(dpy)) {
  953. trace_gd_keymap_windowing("wayland");
  954. *maplen = qemu_input_map_xorgevdev_to_qcode_len;
  955. return qemu_input_map_xorgevdev_to_qcode;
  956. }
  957. #endif
  958. #ifdef GDK_WINDOWING_WIN32
  959. if (GDK_IS_WIN32_DISPLAY(dpy)) {
  960. trace_gd_keymap_windowing("win32");
  961. *maplen = qemu_input_map_win32_to_qcode_len;
  962. return qemu_input_map_win32_to_qcode;
  963. }
  964. #endif
  965. #ifdef GDK_WINDOWING_QUARTZ
  966. if (GDK_IS_QUARTZ_DISPLAY(dpy)) {
  967. trace_gd_keymap_windowing("quartz");
  968. *maplen = qemu_input_map_osx_to_qcode_len;
  969. return qemu_input_map_osx_to_qcode;
  970. }
  971. #endif
  972. #ifdef GDK_WINDOWING_BROADWAY
  973. if (GDK_IS_BROADWAY_DISPLAY(dpy)) {
  974. trace_gd_keymap_windowing("broadway");
  975. g_warning("experimental: using broadway, x11 virtual keysym\n"
  976. "mapping - with very limited support. See also\n"
  977. "https://bugzilla.gnome.org/show_bug.cgi?id=700105");
  978. *maplen = qemu_input_map_x11_to_qcode_len;
  979. return qemu_input_map_x11_to_qcode;
  980. }
  981. #endif
  982. g_warning("Unsupported GDK Windowing platform.\n"
  983. "Disabling extended keycode tables.\n"
  984. "Please report to qemu-devel@nongnu.org\n"
  985. "including the following information:\n"
  986. "\n"
  987. " - Operating system\n"
  988. " - GDK Windowing system build\n");
  989. return NULL;
  990. }
  991. static int gd_map_keycode(int scancode)
  992. {
  993. if (!keycode_map) {
  994. return 0;
  995. }
  996. if (scancode > keycode_maplen) {
  997. return 0;
  998. }
  999. return keycode_map[scancode];
  1000. }
  1001. static gboolean gd_text_key_down(GtkWidget *widget,
  1002. GdkEventKey *key, void *opaque)
  1003. {
  1004. VirtualConsole *vc = opaque;
  1005. QemuConsole *con = vc->gfx.dcl.con;
  1006. if (key->keyval == GDK_KEY_Delete) {
  1007. kbd_put_qcode_console(con, Q_KEY_CODE_DELETE, false);
  1008. } else if (key->length) {
  1009. kbd_put_string_console(con, key->string, key->length);
  1010. } else {
  1011. int qcode = gd_map_keycode(key->hardware_keycode);
  1012. kbd_put_qcode_console(con, qcode, false);
  1013. }
  1014. return TRUE;
  1015. }
  1016. static gboolean gd_key_event(GtkWidget *widget, GdkEventKey *key, void *opaque)
  1017. {
  1018. VirtualConsole *vc = opaque;
  1019. GtkDisplayState *s = vc->s;
  1020. int qcode;
  1021. int i;
  1022. if (s->ignore_keys) {
  1023. s->ignore_keys = (key->type == GDK_KEY_PRESS);
  1024. return TRUE;
  1025. }
  1026. #ifdef WIN32
  1027. /* on windows, we ought to ignore the reserved key event? */
  1028. if (key->hardware_keycode == 0xff)
  1029. return false;
  1030. #endif
  1031. if (key->keyval == GDK_KEY_Pause
  1032. #ifdef G_OS_WIN32
  1033. /* for some reason GDK does not fill keyval for VK_PAUSE
  1034. * See https://bugzilla.gnome.org/show_bug.cgi?id=769214
  1035. */
  1036. || key->hardware_keycode == VK_PAUSE
  1037. #endif
  1038. ) {
  1039. qemu_input_event_send_key_qcode(vc->gfx.dcl.con, Q_KEY_CODE_PAUSE,
  1040. key->type == GDK_KEY_PRESS);
  1041. return TRUE;
  1042. }
  1043. qcode = gd_map_keycode(key->hardware_keycode);
  1044. trace_gd_key_event(vc->label, key->hardware_keycode, qcode,
  1045. (key->type == GDK_KEY_PRESS) ? "down" : "up");
  1046. for (i = 0; i < ARRAY_SIZE(modifier_keycode); i++) {
  1047. if (qcode == modifier_keycode[i]) {
  1048. s->modifier_pressed[i] = (key->type == GDK_KEY_PRESS);
  1049. }
  1050. }
  1051. qemu_input_event_send_key_qcode(vc->gfx.dcl.con, qcode,
  1052. key->type == GDK_KEY_PRESS);
  1053. return TRUE;
  1054. }
  1055. static gboolean gd_event(GtkWidget *widget, GdkEvent *event, void *opaque)
  1056. {
  1057. if (event->type == GDK_MOTION_NOTIFY) {
  1058. return gd_motion_event(widget, &event->motion, opaque);
  1059. }
  1060. return FALSE;
  1061. }
  1062. /** Window Menu Actions **/
  1063. static void gd_menu_pause(GtkMenuItem *item, void *opaque)
  1064. {
  1065. GtkDisplayState *s = opaque;
  1066. if (s->external_pause_update) {
  1067. return;
  1068. }
  1069. if (runstate_is_running()) {
  1070. qmp_stop(NULL);
  1071. } else {
  1072. qmp_cont(NULL);
  1073. }
  1074. }
  1075. static void gd_menu_reset(GtkMenuItem *item, void *opaque)
  1076. {
  1077. qmp_system_reset(NULL);
  1078. }
  1079. static void gd_menu_powerdown(GtkMenuItem *item, void *opaque)
  1080. {
  1081. qmp_system_powerdown(NULL);
  1082. }
  1083. static void gd_menu_quit(GtkMenuItem *item, void *opaque)
  1084. {
  1085. qmp_quit(NULL);
  1086. }
  1087. static void gd_menu_switch_vc(GtkMenuItem *item, void *opaque)
  1088. {
  1089. GtkDisplayState *s = opaque;
  1090. VirtualConsole *vc = gd_vc_find_by_menu(s);
  1091. GtkNotebook *nb = GTK_NOTEBOOK(s->notebook);
  1092. gint page;
  1093. gtk_release_modifiers(s);
  1094. if (vc) {
  1095. page = gtk_notebook_page_num(nb, vc->tab_item);
  1096. gtk_notebook_set_current_page(nb, page);
  1097. gtk_widget_grab_focus(vc->focus);
  1098. }
  1099. s->ignore_keys = false;
  1100. }
  1101. static void gd_accel_switch_vc(void *opaque)
  1102. {
  1103. VirtualConsole *vc = opaque;
  1104. gtk_check_menu_item_set_active(GTK_CHECK_MENU_ITEM(vc->menu_item), TRUE);
  1105. #if !GTK_CHECK_VERSION(3, 0, 0)
  1106. /* GTK2 sends the accel key to the target console - ignore this until */
  1107. vc->s->ignore_keys = true;
  1108. #endif
  1109. }
  1110. static void gd_menu_show_tabs(GtkMenuItem *item, void *opaque)
  1111. {
  1112. GtkDisplayState *s = opaque;
  1113. VirtualConsole *vc = gd_vc_find_current(s);
  1114. if (gtk_check_menu_item_get_active(GTK_CHECK_MENU_ITEM(s->show_tabs_item))) {
  1115. gtk_notebook_set_show_tabs(GTK_NOTEBOOK(s->notebook), TRUE);
  1116. } else {
  1117. gtk_notebook_set_show_tabs(GTK_NOTEBOOK(s->notebook), FALSE);
  1118. }
  1119. gd_update_windowsize(vc);
  1120. }
  1121. static gboolean gd_tab_window_close(GtkWidget *widget, GdkEvent *event,
  1122. void *opaque)
  1123. {
  1124. VirtualConsole *vc = opaque;
  1125. GtkDisplayState *s = vc->s;
  1126. gtk_widget_set_sensitive(vc->menu_item, true);
  1127. gd_widget_reparent(vc->window, s->notebook, vc->tab_item);
  1128. gtk_notebook_set_tab_label_text(GTK_NOTEBOOK(s->notebook),
  1129. vc->tab_item, vc->label);
  1130. gtk_widget_destroy(vc->window);
  1131. vc->window = NULL;
  1132. return TRUE;
  1133. }
  1134. static gboolean gd_win_grab(void *opaque)
  1135. {
  1136. VirtualConsole *vc = opaque;
  1137. fprintf(stderr, "%s: %s\n", __func__, vc->label);
  1138. if (vc->s->ptr_owner) {
  1139. gd_ungrab_pointer(vc->s);
  1140. } else {
  1141. gd_grab_pointer(vc, "user-request-detached-tab");
  1142. }
  1143. return TRUE;
  1144. }
  1145. static void gd_menu_untabify(GtkMenuItem *item, void *opaque)
  1146. {
  1147. GtkDisplayState *s = opaque;
  1148. VirtualConsole *vc = gd_vc_find_current(s);
  1149. if (vc->type == GD_VC_GFX &&
  1150. qemu_console_is_graphic(vc->gfx.dcl.con)) {
  1151. gtk_check_menu_item_set_active(GTK_CHECK_MENU_ITEM(s->grab_item),
  1152. FALSE);
  1153. }
  1154. if (!vc->window) {
  1155. gtk_widget_set_sensitive(vc->menu_item, false);
  1156. vc->window = gtk_window_new(GTK_WINDOW_TOPLEVEL);
  1157. gd_widget_reparent(s->notebook, vc->window, vc->tab_item);
  1158. g_signal_connect(vc->window, "delete-event",
  1159. G_CALLBACK(gd_tab_window_close), vc);
  1160. gtk_widget_show_all(vc->window);
  1161. if (qemu_console_is_graphic(vc->gfx.dcl.con)) {
  1162. GtkAccelGroup *ag = gtk_accel_group_new();
  1163. gtk_window_add_accel_group(GTK_WINDOW(vc->window), ag);
  1164. GClosure *cb = g_cclosure_new_swap(G_CALLBACK(gd_win_grab),
  1165. vc, NULL);
  1166. gtk_accel_group_connect(ag, GDK_KEY_g, HOTKEY_MODIFIERS, 0, cb);
  1167. }
  1168. gd_update_geometry_hints(vc);
  1169. gd_update_caption(s);
  1170. }
  1171. }
  1172. static void gd_menu_show_menubar(GtkMenuItem *item, void *opaque)
  1173. {
  1174. GtkDisplayState *s = opaque;
  1175. VirtualConsole *vc = gd_vc_find_current(s);
  1176. if (s->full_screen) {
  1177. return;
  1178. }
  1179. if (gtk_check_menu_item_get_active(
  1180. GTK_CHECK_MENU_ITEM(s->show_menubar_item))) {
  1181. gtk_widget_show(s->menu_bar);
  1182. } else {
  1183. gtk_widget_hide(s->menu_bar);
  1184. }
  1185. gd_update_windowsize(vc);
  1186. }
  1187. static void gd_accel_show_menubar(void *opaque)
  1188. {
  1189. GtkDisplayState *s = opaque;
  1190. gtk_menu_item_activate(GTK_MENU_ITEM(s->show_menubar_item));
  1191. }
  1192. static void gd_menu_full_screen(GtkMenuItem *item, void *opaque)
  1193. {
  1194. GtkDisplayState *s = opaque;
  1195. VirtualConsole *vc = gd_vc_find_current(s);
  1196. if (!s->full_screen) {
  1197. gtk_notebook_set_show_tabs(GTK_NOTEBOOK(s->notebook), FALSE);
  1198. gtk_widget_hide(s->menu_bar);
  1199. if (vc->type == GD_VC_GFX) {
  1200. gtk_widget_set_size_request(vc->gfx.drawing_area, -1, -1);
  1201. }
  1202. gtk_window_fullscreen(GTK_WINDOW(s->window));
  1203. s->full_screen = TRUE;
  1204. } else {
  1205. gtk_window_unfullscreen(GTK_WINDOW(s->window));
  1206. gd_menu_show_tabs(GTK_MENU_ITEM(s->show_tabs_item), s);
  1207. if (gtk_check_menu_item_get_active(
  1208. GTK_CHECK_MENU_ITEM(s->show_menubar_item))) {
  1209. gtk_widget_show(s->menu_bar);
  1210. }
  1211. s->full_screen = FALSE;
  1212. if (vc->type == GD_VC_GFX) {
  1213. vc->gfx.scale_x = 1.0;
  1214. vc->gfx.scale_y = 1.0;
  1215. gd_update_windowsize(vc);
  1216. }
  1217. }
  1218. gd_update_cursor(vc);
  1219. }
  1220. static void gd_accel_full_screen(void *opaque)
  1221. {
  1222. GtkDisplayState *s = opaque;
  1223. gtk_menu_item_activate(GTK_MENU_ITEM(s->full_screen_item));
  1224. }
  1225. static void gd_menu_zoom_in(GtkMenuItem *item, void *opaque)
  1226. {
  1227. GtkDisplayState *s = opaque;
  1228. VirtualConsole *vc = gd_vc_find_current(s);
  1229. gtk_check_menu_item_set_active(GTK_CHECK_MENU_ITEM(s->zoom_fit_item),
  1230. FALSE);
  1231. vc->gfx.scale_x += VC_SCALE_STEP;
  1232. vc->gfx.scale_y += VC_SCALE_STEP;
  1233. gd_update_windowsize(vc);
  1234. }
  1235. static void gd_accel_zoom_in(void *opaque)
  1236. {
  1237. GtkDisplayState *s = opaque;
  1238. gtk_menu_item_activate(GTK_MENU_ITEM(s->zoom_in_item));
  1239. }
  1240. static void gd_menu_zoom_out(GtkMenuItem *item, void *opaque)
  1241. {
  1242. GtkDisplayState *s = opaque;
  1243. VirtualConsole *vc = gd_vc_find_current(s);
  1244. gtk_check_menu_item_set_active(GTK_CHECK_MENU_ITEM(s->zoom_fit_item),
  1245. FALSE);
  1246. vc->gfx.scale_x -= VC_SCALE_STEP;
  1247. vc->gfx.scale_y -= VC_SCALE_STEP;
  1248. vc->gfx.scale_x = MAX(vc->gfx.scale_x, VC_SCALE_MIN);
  1249. vc->gfx.scale_y = MAX(vc->gfx.scale_y, VC_SCALE_MIN);
  1250. gd_update_windowsize(vc);
  1251. }
  1252. static void gd_menu_zoom_fixed(GtkMenuItem *item, void *opaque)
  1253. {
  1254. GtkDisplayState *s = opaque;
  1255. VirtualConsole *vc = gd_vc_find_current(s);
  1256. vc->gfx.scale_x = 1.0;
  1257. vc->gfx.scale_y = 1.0;
  1258. gd_update_windowsize(vc);
  1259. }
  1260. static void gd_menu_zoom_fit(GtkMenuItem *item, void *opaque)
  1261. {
  1262. GtkDisplayState *s = opaque;
  1263. VirtualConsole *vc = gd_vc_find_current(s);
  1264. if (gtk_check_menu_item_get_active(GTK_CHECK_MENU_ITEM(s->zoom_fit_item))) {
  1265. s->free_scale = TRUE;
  1266. } else {
  1267. s->free_scale = FALSE;
  1268. vc->gfx.scale_x = 1.0;
  1269. vc->gfx.scale_y = 1.0;
  1270. }
  1271. gd_update_windowsize(vc);
  1272. gd_update_full_redraw(vc);
  1273. }
  1274. #if GTK_CHECK_VERSION(3, 20, 0)
  1275. static void gd_grab_update(VirtualConsole *vc, bool kbd, bool ptr)
  1276. {
  1277. GdkDisplay *display = gtk_widget_get_display(vc->gfx.drawing_area);
  1278. GdkSeat *seat = gdk_display_get_default_seat(display);
  1279. GdkWindow *window = gtk_widget_get_window(vc->gfx.drawing_area);
  1280. GdkSeatCapabilities caps = 0;
  1281. GdkCursor *cursor = NULL;
  1282. if (kbd) {
  1283. caps |= GDK_SEAT_CAPABILITY_KEYBOARD;
  1284. }
  1285. if (ptr) {
  1286. caps |= GDK_SEAT_CAPABILITY_ALL_POINTING;
  1287. cursor = vc->s->null_cursor;
  1288. }
  1289. if (caps) {
  1290. gdk_seat_grab(seat, window, caps, false, cursor,
  1291. NULL, NULL, NULL);
  1292. } else {
  1293. gdk_seat_ungrab(seat);
  1294. }
  1295. }
  1296. #elif GTK_CHECK_VERSION(3, 0, 0)
  1297. static void gd_grab_devices(VirtualConsole *vc, bool grab,
  1298. GdkInputSource source, GdkEventMask mask,
  1299. GdkCursor *cursor)
  1300. {
  1301. GdkDisplay *display = gtk_widget_get_display(vc->gfx.drawing_area);
  1302. GdkDeviceManager *mgr = gdk_display_get_device_manager(display);
  1303. GList *devs = gdk_device_manager_list_devices(mgr, GDK_DEVICE_TYPE_MASTER);
  1304. GList *tmp = devs;
  1305. for (tmp = devs; tmp; tmp = tmp->next) {
  1306. GdkDevice *dev = tmp->data;
  1307. if (gdk_device_get_source(dev) != source) {
  1308. continue;
  1309. }
  1310. if (grab) {
  1311. GdkWindow *win = gtk_widget_get_window(vc->gfx.drawing_area);
  1312. gdk_device_grab(dev, win, GDK_OWNERSHIP_NONE, FALSE,
  1313. mask, cursor, GDK_CURRENT_TIME);
  1314. } else {
  1315. gdk_device_ungrab(dev, GDK_CURRENT_TIME);
  1316. }
  1317. }
  1318. g_list_free(devs);
  1319. }
  1320. #endif
  1321. static void gd_grab_keyboard(VirtualConsole *vc, const char *reason)
  1322. {
  1323. if (vc->s->kbd_owner) {
  1324. if (vc->s->kbd_owner == vc) {
  1325. return;
  1326. } else {
  1327. gd_ungrab_keyboard(vc->s);
  1328. }
  1329. }
  1330. #if GTK_CHECK_VERSION(3, 20, 0)
  1331. gd_grab_update(vc, true, vc->s->ptr_owner == vc);
  1332. #elif GTK_CHECK_VERSION(3, 0, 0)
  1333. gd_grab_devices(vc, true, GDK_SOURCE_KEYBOARD,
  1334. GDK_KEY_PRESS_MASK | GDK_KEY_RELEASE_MASK,
  1335. NULL);
  1336. #else
  1337. gdk_keyboard_grab(gtk_widget_get_window(vc->gfx.drawing_area),
  1338. FALSE,
  1339. GDK_CURRENT_TIME);
  1340. #endif
  1341. vc->s->kbd_owner = vc;
  1342. gd_update_caption(vc->s);
  1343. trace_gd_grab(vc->label, "kbd", reason);
  1344. }
  1345. static void gd_ungrab_keyboard(GtkDisplayState *s)
  1346. {
  1347. VirtualConsole *vc = s->kbd_owner;
  1348. if (vc == NULL) {
  1349. return;
  1350. }
  1351. s->kbd_owner = NULL;
  1352. #if GTK_CHECK_VERSION(3, 20, 0)
  1353. gd_grab_update(vc, false, vc->s->ptr_owner == vc);
  1354. #elif GTK_CHECK_VERSION(3, 0, 0)
  1355. gd_grab_devices(vc, false, GDK_SOURCE_KEYBOARD, 0, NULL);
  1356. #else
  1357. gdk_keyboard_ungrab(GDK_CURRENT_TIME);
  1358. #endif
  1359. gd_update_caption(s);
  1360. trace_gd_ungrab(vc->label, "kbd");
  1361. }
  1362. static void gd_grab_pointer(VirtualConsole *vc, const char *reason)
  1363. {
  1364. GdkDisplay *display = gtk_widget_get_display(vc->gfx.drawing_area);
  1365. if (vc->s->ptr_owner) {
  1366. if (vc->s->ptr_owner == vc) {
  1367. return;
  1368. } else {
  1369. gd_ungrab_pointer(vc->s);
  1370. }
  1371. }
  1372. #if GTK_CHECK_VERSION(3, 20, 0)
  1373. gd_grab_update(vc, vc->s->kbd_owner == vc, true);
  1374. gdk_device_get_position(gd_get_pointer(display),
  1375. NULL, &vc->s->grab_x_root, &vc->s->grab_y_root);
  1376. #elif GTK_CHECK_VERSION(3, 0, 0)
  1377. gd_grab_devices(vc, true, GDK_SOURCE_MOUSE,
  1378. GDK_POINTER_MOTION_MASK |
  1379. GDK_BUTTON_PRESS_MASK |
  1380. GDK_BUTTON_RELEASE_MASK |
  1381. GDK_BUTTON_MOTION_MASK |
  1382. GDK_SCROLL_MASK,
  1383. vc->s->null_cursor);
  1384. gdk_device_get_position(gd_get_pointer(display),
  1385. NULL, &vc->s->grab_x_root, &vc->s->grab_y_root);
  1386. #else
  1387. gdk_pointer_grab(gtk_widget_get_window(vc->gfx.drawing_area),
  1388. FALSE, /* All events to come to our window directly */
  1389. GDK_POINTER_MOTION_MASK |
  1390. GDK_BUTTON_PRESS_MASK |
  1391. GDK_BUTTON_RELEASE_MASK |
  1392. GDK_BUTTON_MOTION_MASK |
  1393. GDK_SCROLL_MASK,
  1394. NULL, /* Allow cursor to move over entire desktop */
  1395. vc->s->null_cursor,
  1396. GDK_CURRENT_TIME);
  1397. gdk_display_get_pointer(display, NULL,
  1398. &vc->s->grab_x_root, &vc->s->grab_y_root, NULL);
  1399. #endif
  1400. vc->s->ptr_owner = vc;
  1401. gd_update_caption(vc->s);
  1402. trace_gd_grab(vc->label, "ptr", reason);
  1403. }
  1404. static void gd_ungrab_pointer(GtkDisplayState *s)
  1405. {
  1406. VirtualConsole *vc = s->ptr_owner;
  1407. GdkDisplay *display;
  1408. if (vc == NULL) {
  1409. return;
  1410. }
  1411. s->ptr_owner = NULL;
  1412. display = gtk_widget_get_display(vc->gfx.drawing_area);
  1413. #if GTK_CHECK_VERSION(3, 20, 0)
  1414. gd_grab_update(vc, vc->s->kbd_owner == vc, false);
  1415. gdk_device_warp(gd_get_pointer(display),
  1416. gtk_widget_get_screen(vc->gfx.drawing_area),
  1417. vc->s->grab_x_root, vc->s->grab_y_root);
  1418. #elif GTK_CHECK_VERSION(3, 0, 0)
  1419. gd_grab_devices(vc, false, GDK_SOURCE_MOUSE, 0, NULL);
  1420. gdk_device_warp(gd_get_pointer(display),
  1421. gtk_widget_get_screen(vc->gfx.drawing_area),
  1422. vc->s->grab_x_root, vc->s->grab_y_root);
  1423. #else
  1424. gdk_pointer_ungrab(GDK_CURRENT_TIME);
  1425. gdk_display_warp_pointer(display,
  1426. gtk_widget_get_screen(vc->gfx.drawing_area),
  1427. vc->s->grab_x_root, vc->s->grab_y_root);
  1428. #endif
  1429. gd_update_caption(s);
  1430. trace_gd_ungrab(vc->label, "ptr");
  1431. }
  1432. static void gd_menu_grab_input(GtkMenuItem *item, void *opaque)
  1433. {
  1434. GtkDisplayState *s = opaque;
  1435. VirtualConsole *vc = gd_vc_find_current(s);
  1436. if (gd_is_grab_active(s)) {
  1437. gd_grab_keyboard(vc, "user-request-main-window");
  1438. gd_grab_pointer(vc, "user-request-main-window");
  1439. } else {
  1440. gd_ungrab_keyboard(s);
  1441. gd_ungrab_pointer(s);
  1442. }
  1443. gd_update_cursor(vc);
  1444. }
  1445. static void gd_change_page(GtkNotebook *nb, gpointer arg1, guint arg2,
  1446. gpointer data)
  1447. {
  1448. GtkDisplayState *s = data;
  1449. VirtualConsole *vc;
  1450. gboolean on_vga;
  1451. if (!gtk_widget_get_realized(s->notebook)) {
  1452. return;
  1453. }
  1454. #ifdef VTE_RESIZE_HACK
  1455. vc = gd_vc_find_current(s);
  1456. if (vc && vc->type == GD_VC_VTE) {
  1457. gtk_widget_hide(vc->vte.terminal);
  1458. }
  1459. #endif
  1460. vc = gd_vc_find_by_page(s, arg2);
  1461. if (!vc) {
  1462. return;
  1463. }
  1464. #ifdef VTE_RESIZE_HACK
  1465. if (vc->type == GD_VC_VTE) {
  1466. gtk_widget_show(vc->vte.terminal);
  1467. }
  1468. #endif
  1469. gtk_check_menu_item_set_active(GTK_CHECK_MENU_ITEM(vc->menu_item),
  1470. TRUE);
  1471. on_vga = (vc->type == GD_VC_GFX &&
  1472. qemu_console_is_graphic(vc->gfx.dcl.con));
  1473. if (!on_vga) {
  1474. gtk_check_menu_item_set_active(GTK_CHECK_MENU_ITEM(s->grab_item),
  1475. FALSE);
  1476. } else if (s->full_screen) {
  1477. gtk_check_menu_item_set_active(GTK_CHECK_MENU_ITEM(s->grab_item),
  1478. TRUE);
  1479. }
  1480. gtk_widget_set_sensitive(s->grab_item, on_vga);
  1481. #ifdef CONFIG_VTE
  1482. gtk_widget_set_sensitive(s->copy_item, vc->type == GD_VC_VTE);
  1483. #endif
  1484. gd_update_windowsize(vc);
  1485. gd_update_cursor(vc);
  1486. }
  1487. static gboolean gd_enter_event(GtkWidget *widget, GdkEventCrossing *crossing,
  1488. gpointer opaque)
  1489. {
  1490. VirtualConsole *vc = opaque;
  1491. GtkDisplayState *s = vc->s;
  1492. if (gd_grab_on_hover(s)) {
  1493. gd_grab_keyboard(vc, "grab-on-hover");
  1494. }
  1495. return TRUE;
  1496. }
  1497. static gboolean gd_leave_event(GtkWidget *widget, GdkEventCrossing *crossing,
  1498. gpointer opaque)
  1499. {
  1500. VirtualConsole *vc = opaque;
  1501. GtkDisplayState *s = vc->s;
  1502. if (gd_grab_on_hover(s)) {
  1503. gd_ungrab_keyboard(s);
  1504. }
  1505. return TRUE;
  1506. }
  1507. static gboolean gd_focus_out_event(GtkWidget *widget,
  1508. GdkEventCrossing *crossing, gpointer opaque)
  1509. {
  1510. VirtualConsole *vc = opaque;
  1511. GtkDisplayState *s = vc->s;
  1512. gtk_release_modifiers(s);
  1513. return TRUE;
  1514. }
  1515. static gboolean gd_configure(GtkWidget *widget,
  1516. GdkEventConfigure *cfg, gpointer opaque)
  1517. {
  1518. VirtualConsole *vc = opaque;
  1519. gd_set_ui_info(vc, cfg->width, cfg->height);
  1520. return FALSE;
  1521. }
  1522. /** Virtual Console Callbacks **/
  1523. static GSList *gd_vc_menu_init(GtkDisplayState *s, VirtualConsole *vc,
  1524. int idx, GSList *group, GtkWidget *view_menu)
  1525. {
  1526. vc->menu_item = gtk_radio_menu_item_new_with_mnemonic(group, vc->label);
  1527. gtk_accel_group_connect(s->accel_group, GDK_KEY_1 + idx,
  1528. HOTKEY_MODIFIERS, 0,
  1529. g_cclosure_new_swap(G_CALLBACK(gd_accel_switch_vc), vc, NULL));
  1530. #if GTK_CHECK_VERSION(3, 8, 0)
  1531. gtk_accel_label_set_accel(
  1532. GTK_ACCEL_LABEL(gtk_bin_get_child(GTK_BIN(vc->menu_item))),
  1533. GDK_KEY_1 + idx, HOTKEY_MODIFIERS);
  1534. #endif
  1535. g_signal_connect(vc->menu_item, "activate",
  1536. G_CALLBACK(gd_menu_switch_vc), s);
  1537. gtk_menu_shell_append(GTK_MENU_SHELL(view_menu), vc->menu_item);
  1538. group = gtk_radio_menu_item_get_group(GTK_RADIO_MENU_ITEM(vc->menu_item));
  1539. return group;
  1540. }
  1541. #if defined(CONFIG_VTE)
  1542. static void gd_menu_copy(GtkMenuItem *item, void *opaque)
  1543. {
  1544. GtkDisplayState *s = opaque;
  1545. VirtualConsole *vc = gd_vc_find_current(s);
  1546. #if VTE_CHECK_VERSION(0, 50, 0)
  1547. vte_terminal_copy_clipboard_format(VTE_TERMINAL(vc->vte.terminal),
  1548. VTE_FORMAT_TEXT);
  1549. #else
  1550. vte_terminal_copy_clipboard(VTE_TERMINAL(vc->vte.terminal));
  1551. #endif
  1552. }
  1553. static void gd_vc_adjustment_changed(GtkAdjustment *adjustment, void *opaque)
  1554. {
  1555. VirtualConsole *vc = opaque;
  1556. if (gtk_adjustment_get_upper(adjustment) >
  1557. gtk_adjustment_get_page_size(adjustment)) {
  1558. gtk_widget_show(vc->vte.scrollbar);
  1559. } else {
  1560. gtk_widget_hide(vc->vte.scrollbar);
  1561. }
  1562. }
  1563. static int gd_vc_chr_write(Chardev *chr, const uint8_t *buf, int len)
  1564. {
  1565. VCChardev *vcd = VC_CHARDEV(chr);
  1566. VirtualConsole *vc = vcd->console;
  1567. vte_terminal_feed(VTE_TERMINAL(vc->vte.terminal), (const char *)buf, len);
  1568. return len;
  1569. }
  1570. static void gd_vc_chr_set_echo(Chardev *chr, bool echo)
  1571. {
  1572. VCChardev *vcd = VC_CHARDEV(chr);
  1573. VirtualConsole *vc = vcd->console;
  1574. if (vc) {
  1575. vc->vte.echo = echo;
  1576. } else {
  1577. vcd->echo = echo;
  1578. }
  1579. }
  1580. static int nb_vcs;
  1581. static Chardev *vcs[MAX_VCS];
  1582. static void gd_vc_open(Chardev *chr,
  1583. ChardevBackend *backend,
  1584. bool *be_opened,
  1585. Error **errp)
  1586. {
  1587. if (nb_vcs == MAX_VCS) {
  1588. error_setg(errp, "Maximum number of consoles reached");
  1589. return;
  1590. }
  1591. vcs[nb_vcs++] = chr;
  1592. /* console/chardev init sometimes completes elsewhere in a 2nd
  1593. * stage, so defer OPENED events until they are fully initialized
  1594. */
  1595. *be_opened = false;
  1596. }
  1597. static void char_gd_vc_class_init(ObjectClass *oc, void *data)
  1598. {
  1599. ChardevClass *cc = CHARDEV_CLASS(oc);
  1600. cc->parse = qemu_chr_parse_vc;
  1601. cc->open = gd_vc_open;
  1602. cc->chr_write = gd_vc_chr_write;
  1603. cc->chr_set_echo = gd_vc_chr_set_echo;
  1604. }
  1605. static const TypeInfo char_gd_vc_type_info = {
  1606. .name = TYPE_CHARDEV_VC,
  1607. .parent = TYPE_CHARDEV,
  1608. .instance_size = sizeof(VCChardev),
  1609. .class_init = char_gd_vc_class_init,
  1610. };
  1611. static gboolean gd_vc_in(VteTerminal *terminal, gchar *text, guint size,
  1612. gpointer user_data)
  1613. {
  1614. VirtualConsole *vc = user_data;
  1615. if (vc->vte.echo) {
  1616. VteTerminal *term = VTE_TERMINAL(vc->vte.terminal);
  1617. int i;
  1618. for (i = 0; i < size; i++) {
  1619. uint8_t c = text[i];
  1620. if (c >= 128 || isprint(c)) {
  1621. /* 8-bit characters are considered printable. */
  1622. vte_terminal_feed(term, &text[i], 1);
  1623. } else if (c == '\r' || c == '\n') {
  1624. vte_terminal_feed(term, "\r\n", 2);
  1625. } else {
  1626. char ctrl[2] = { '^', 0};
  1627. ctrl[1] = text[i] ^ 64;
  1628. vte_terminal_feed(term, ctrl, 2);
  1629. }
  1630. }
  1631. }
  1632. qemu_chr_be_write(vc->vte.chr, (uint8_t *)text, (unsigned int)size);
  1633. return TRUE;
  1634. }
  1635. static GSList *gd_vc_vte_init(GtkDisplayState *s, VirtualConsole *vc,
  1636. Chardev *chr, int idx,
  1637. GSList *group, GtkWidget *view_menu)
  1638. {
  1639. char buffer[32];
  1640. GtkWidget *box;
  1641. GtkWidget *scrollbar;
  1642. GtkAdjustment *vadjustment;
  1643. VCChardev *vcd = VC_CHARDEV(chr);
  1644. vc->s = s;
  1645. vc->vte.echo = vcd->echo;
  1646. vc->vte.chr = chr;
  1647. vcd->console = vc;
  1648. snprintf(buffer, sizeof(buffer), "vc%d", idx);
  1649. vc->label = g_strdup_printf("%s", vc->vte.chr->label
  1650. ? vc->vte.chr->label : buffer);
  1651. group = gd_vc_menu_init(s, vc, idx, group, view_menu);
  1652. vc->vte.terminal = vte_terminal_new();
  1653. g_signal_connect(vc->vte.terminal, "commit", G_CALLBACK(gd_vc_in), vc);
  1654. /* The documentation says that the default is UTF-8, but actually it is
  1655. * 7-bit ASCII at least in VTE 0.38.
  1656. */
  1657. #if VTE_CHECK_VERSION(0, 38, 0)
  1658. vte_terminal_set_encoding(VTE_TERMINAL(vc->vte.terminal), "UTF-8", NULL);
  1659. #else
  1660. vte_terminal_set_encoding(VTE_TERMINAL(vc->vte.terminal), "UTF-8");
  1661. #endif
  1662. vte_terminal_set_scrollback_lines(VTE_TERMINAL(vc->vte.terminal), -1);
  1663. vte_terminal_set_size(VTE_TERMINAL(vc->vte.terminal),
  1664. VC_TERM_X_MIN, VC_TERM_Y_MIN);
  1665. #if VTE_CHECK_VERSION(0, 28, 0) && GTK_CHECK_VERSION(3, 0, 0)
  1666. vadjustment = gtk_scrollable_get_vadjustment
  1667. (GTK_SCROLLABLE(vc->vte.terminal));
  1668. #else
  1669. vadjustment = vte_terminal_get_adjustment(VTE_TERMINAL(vc->vte.terminal));
  1670. #endif
  1671. #if GTK_CHECK_VERSION(3, 0, 0)
  1672. box = gtk_box_new(GTK_ORIENTATION_HORIZONTAL, 2);
  1673. scrollbar = gtk_scrollbar_new(GTK_ORIENTATION_VERTICAL, vadjustment);
  1674. #else
  1675. box = gtk_hbox_new(false, 2);
  1676. scrollbar = gtk_vscrollbar_new(vadjustment);
  1677. #endif
  1678. gtk_box_pack_end(GTK_BOX(box), scrollbar, FALSE, FALSE, 0);
  1679. gtk_box_pack_end(GTK_BOX(box), vc->vte.terminal, TRUE, TRUE, 0);
  1680. vc->vte.box = box;
  1681. vc->vte.scrollbar = scrollbar;
  1682. g_signal_connect(vadjustment, "changed",
  1683. G_CALLBACK(gd_vc_adjustment_changed), vc);
  1684. vc->type = GD_VC_VTE;
  1685. vc->tab_item = box;
  1686. vc->focus = vc->vte.terminal;
  1687. gtk_notebook_append_page(GTK_NOTEBOOK(s->notebook), vc->tab_item,
  1688. gtk_label_new(vc->label));
  1689. qemu_chr_be_event(vc->vte.chr, CHR_EVENT_OPENED);
  1690. return group;
  1691. }
  1692. static void gd_vcs_init(GtkDisplayState *s, GSList *group,
  1693. GtkWidget *view_menu)
  1694. {
  1695. int i;
  1696. for (i = 0; i < nb_vcs; i++) {
  1697. VirtualConsole *vc = &s->vc[s->nb_vcs];
  1698. group = gd_vc_vte_init(s, vc, vcs[i], s->nb_vcs, group, view_menu);
  1699. s->nb_vcs++;
  1700. }
  1701. }
  1702. #endif /* CONFIG_VTE */
  1703. /** Window Creation **/
  1704. static void gd_connect_vc_gfx_signals(VirtualConsole *vc)
  1705. {
  1706. #if GTK_CHECK_VERSION(3, 0, 0)
  1707. g_signal_connect(vc->gfx.drawing_area, "draw",
  1708. G_CALLBACK(gd_draw_event), vc);
  1709. #if defined(CONFIG_GTK_GL)
  1710. if (gtk_use_gl_area) {
  1711. /* wire up GtkGlArea events */
  1712. g_signal_connect(vc->gfx.drawing_area, "render",
  1713. G_CALLBACK(gd_render_event), vc);
  1714. g_signal_connect(vc->gfx.drawing_area, "resize",
  1715. G_CALLBACK(gd_resize_event), vc);
  1716. }
  1717. #endif
  1718. #else
  1719. g_signal_connect(vc->gfx.drawing_area, "expose-event",
  1720. G_CALLBACK(gd_expose_event), vc);
  1721. #endif
  1722. if (qemu_console_is_graphic(vc->gfx.dcl.con)) {
  1723. g_signal_connect(vc->gfx.drawing_area, "event",
  1724. G_CALLBACK(gd_event), vc);
  1725. g_signal_connect(vc->gfx.drawing_area, "button-press-event",
  1726. G_CALLBACK(gd_button_event), vc);
  1727. g_signal_connect(vc->gfx.drawing_area, "button-release-event",
  1728. G_CALLBACK(gd_button_event), vc);
  1729. g_signal_connect(vc->gfx.drawing_area, "scroll-event",
  1730. G_CALLBACK(gd_scroll_event), vc);
  1731. g_signal_connect(vc->gfx.drawing_area, "key-press-event",
  1732. G_CALLBACK(gd_key_event), vc);
  1733. g_signal_connect(vc->gfx.drawing_area, "key-release-event",
  1734. G_CALLBACK(gd_key_event), vc);
  1735. g_signal_connect(vc->gfx.drawing_area, "enter-notify-event",
  1736. G_CALLBACK(gd_enter_event), vc);
  1737. g_signal_connect(vc->gfx.drawing_area, "leave-notify-event",
  1738. G_CALLBACK(gd_leave_event), vc);
  1739. g_signal_connect(vc->gfx.drawing_area, "focus-out-event",
  1740. G_CALLBACK(gd_focus_out_event), vc);
  1741. g_signal_connect(vc->gfx.drawing_area, "configure-event",
  1742. G_CALLBACK(gd_configure), vc);
  1743. } else {
  1744. g_signal_connect(vc->gfx.drawing_area, "key-press-event",
  1745. G_CALLBACK(gd_text_key_down), vc);
  1746. }
  1747. }
  1748. static void gd_connect_signals(GtkDisplayState *s)
  1749. {
  1750. g_signal_connect(s->show_tabs_item, "activate",
  1751. G_CALLBACK(gd_menu_show_tabs), s);
  1752. g_signal_connect(s->untabify_item, "activate",
  1753. G_CALLBACK(gd_menu_untabify), s);
  1754. g_signal_connect(s->show_menubar_item, "activate",
  1755. G_CALLBACK(gd_menu_show_menubar), s);
  1756. g_signal_connect(s->window, "delete-event",
  1757. G_CALLBACK(gd_window_close), s);
  1758. g_signal_connect(s->pause_item, "activate",
  1759. G_CALLBACK(gd_menu_pause), s);
  1760. g_signal_connect(s->reset_item, "activate",
  1761. G_CALLBACK(gd_menu_reset), s);
  1762. g_signal_connect(s->powerdown_item, "activate",
  1763. G_CALLBACK(gd_menu_powerdown), s);
  1764. g_signal_connect(s->quit_item, "activate",
  1765. G_CALLBACK(gd_menu_quit), s);
  1766. #if defined(CONFIG_VTE)
  1767. g_signal_connect(s->copy_item, "activate",
  1768. G_CALLBACK(gd_menu_copy), s);
  1769. #endif
  1770. g_signal_connect(s->full_screen_item, "activate",
  1771. G_CALLBACK(gd_menu_full_screen), s);
  1772. g_signal_connect(s->zoom_in_item, "activate",
  1773. G_CALLBACK(gd_menu_zoom_in), s);
  1774. g_signal_connect(s->zoom_out_item, "activate",
  1775. G_CALLBACK(gd_menu_zoom_out), s);
  1776. g_signal_connect(s->zoom_fixed_item, "activate",
  1777. G_CALLBACK(gd_menu_zoom_fixed), s);
  1778. g_signal_connect(s->zoom_fit_item, "activate",
  1779. G_CALLBACK(gd_menu_zoom_fit), s);
  1780. g_signal_connect(s->grab_item, "activate",
  1781. G_CALLBACK(gd_menu_grab_input), s);
  1782. g_signal_connect(s->notebook, "switch-page",
  1783. G_CALLBACK(gd_change_page), s);
  1784. }
  1785. static GtkWidget *gd_create_menu_machine(GtkDisplayState *s)
  1786. {
  1787. GtkWidget *machine_menu;
  1788. GtkWidget *separator;
  1789. machine_menu = gtk_menu_new();
  1790. gtk_menu_set_accel_group(GTK_MENU(machine_menu), s->accel_group);
  1791. s->pause_item = gtk_check_menu_item_new_with_mnemonic(_("_Pause"));
  1792. gtk_menu_shell_append(GTK_MENU_SHELL(machine_menu), s->pause_item);
  1793. separator = gtk_separator_menu_item_new();
  1794. gtk_menu_shell_append(GTK_MENU_SHELL(machine_menu), separator);
  1795. s->reset_item = gtk_menu_item_new_with_mnemonic(_("_Reset"));
  1796. gtk_menu_shell_append(GTK_MENU_SHELL(machine_menu), s->reset_item);
  1797. s->powerdown_item = gtk_menu_item_new_with_mnemonic(_("Power _Down"));
  1798. gtk_menu_shell_append(GTK_MENU_SHELL(machine_menu), s->powerdown_item);
  1799. separator = gtk_separator_menu_item_new();
  1800. gtk_menu_shell_append(GTK_MENU_SHELL(machine_menu), separator);
  1801. s->quit_item = gtk_menu_item_new_with_mnemonic(_("_Quit"));
  1802. gtk_menu_item_set_accel_path(GTK_MENU_ITEM(s->quit_item),
  1803. "<QEMU>/Machine/Quit");
  1804. gtk_accel_map_add_entry("<QEMU>/Machine/Quit",
  1805. GDK_KEY_q, HOTKEY_MODIFIERS);
  1806. gtk_menu_shell_append(GTK_MENU_SHELL(machine_menu), s->quit_item);
  1807. return machine_menu;
  1808. }
  1809. static GSList *gd_vc_gfx_init(GtkDisplayState *s, VirtualConsole *vc,
  1810. QemuConsole *con, int idx,
  1811. GSList *group, GtkWidget *view_menu)
  1812. {
  1813. vc->label = qemu_console_get_label(con);
  1814. vc->s = s;
  1815. vc->gfx.scale_x = 1.0;
  1816. vc->gfx.scale_y = 1.0;
  1817. #if defined(CONFIG_OPENGL)
  1818. if (display_opengl) {
  1819. #if defined(CONFIG_GTK_GL)
  1820. if (gtk_use_gl_area) {
  1821. vc->gfx.drawing_area = gtk_gl_area_new();
  1822. vc->gfx.dcl.ops = &dcl_gl_area_ops;
  1823. } else
  1824. #endif /* CONFIG_GTK_GL */
  1825. {
  1826. vc->gfx.drawing_area = gtk_drawing_area_new();
  1827. /*
  1828. * gtk_widget_set_double_buffered() was deprecated in 3.14.
  1829. * It is required for opengl rendering on X11 though. A
  1830. * proper replacement (native opengl support) is only
  1831. * available in 3.16+. Silence the warning if possible.
  1832. */
  1833. #ifdef CONFIG_PRAGMA_DIAGNOSTIC_AVAILABLE
  1834. #pragma GCC diagnostic push
  1835. #pragma GCC diagnostic ignored "-Wdeprecated-declarations"
  1836. #endif
  1837. gtk_widget_set_double_buffered(vc->gfx.drawing_area, FALSE);
  1838. #ifdef CONFIG_PRAGMA_DIAGNOSTIC_AVAILABLE
  1839. #pragma GCC diagnostic pop
  1840. #endif
  1841. vc->gfx.dcl.ops = &dcl_egl_ops;
  1842. }
  1843. } else
  1844. #endif
  1845. {
  1846. vc->gfx.drawing_area = gtk_drawing_area_new();
  1847. vc->gfx.dcl.ops = &dcl_ops;
  1848. }
  1849. gtk_widget_add_events(vc->gfx.drawing_area,
  1850. GDK_POINTER_MOTION_MASK |
  1851. GDK_BUTTON_PRESS_MASK |
  1852. GDK_BUTTON_RELEASE_MASK |
  1853. GDK_BUTTON_MOTION_MASK |
  1854. GDK_ENTER_NOTIFY_MASK |
  1855. GDK_LEAVE_NOTIFY_MASK |
  1856. GDK_SCROLL_MASK |
  1857. GDK_KEY_PRESS_MASK);
  1858. gtk_widget_set_can_focus(vc->gfx.drawing_area, TRUE);
  1859. vc->type = GD_VC_GFX;
  1860. vc->tab_item = vc->gfx.drawing_area;
  1861. vc->focus = vc->gfx.drawing_area;
  1862. gtk_notebook_append_page(GTK_NOTEBOOK(s->notebook),
  1863. vc->tab_item, gtk_label_new(vc->label));
  1864. vc->gfx.dcl.con = con;
  1865. register_displaychangelistener(&vc->gfx.dcl);
  1866. gd_connect_vc_gfx_signals(vc);
  1867. group = gd_vc_menu_init(s, vc, idx, group, view_menu);
  1868. if (dpy_ui_info_supported(vc->gfx.dcl.con)) {
  1869. gtk_menu_item_activate(GTK_MENU_ITEM(s->zoom_fit_item));
  1870. s->free_scale = true;
  1871. }
  1872. return group;
  1873. }
  1874. static GtkWidget *gd_create_menu_view(GtkDisplayState *s)
  1875. {
  1876. GSList *group = NULL;
  1877. GtkWidget *view_menu;
  1878. GtkWidget *separator;
  1879. QemuConsole *con;
  1880. int vc;
  1881. view_menu = gtk_menu_new();
  1882. gtk_menu_set_accel_group(GTK_MENU(view_menu), s->accel_group);
  1883. s->full_screen_item = gtk_menu_item_new_with_mnemonic(_("_Fullscreen"));
  1884. #if defined(CONFIG_VTE)
  1885. s->copy_item = gtk_menu_item_new_with_mnemonic(_("_Copy"));
  1886. gtk_menu_shell_append(GTK_MENU_SHELL(view_menu), s->copy_item);
  1887. #endif
  1888. gtk_accel_group_connect(s->accel_group, GDK_KEY_f, HOTKEY_MODIFIERS, 0,
  1889. g_cclosure_new_swap(G_CALLBACK(gd_accel_full_screen), s, NULL));
  1890. #if GTK_CHECK_VERSION(3, 8, 0)
  1891. gtk_accel_label_set_accel(
  1892. GTK_ACCEL_LABEL(gtk_bin_get_child(GTK_BIN(s->full_screen_item))),
  1893. GDK_KEY_f, HOTKEY_MODIFIERS);
  1894. #endif
  1895. gtk_menu_shell_append(GTK_MENU_SHELL(view_menu), s->full_screen_item);
  1896. separator = gtk_separator_menu_item_new();
  1897. gtk_menu_shell_append(GTK_MENU_SHELL(view_menu), separator);
  1898. s->zoom_in_item = gtk_menu_item_new_with_mnemonic(_("Zoom _In"));
  1899. gtk_menu_item_set_accel_path(GTK_MENU_ITEM(s->zoom_in_item),
  1900. "<QEMU>/View/Zoom In");
  1901. gtk_accel_map_add_entry("<QEMU>/View/Zoom In", GDK_KEY_plus,
  1902. HOTKEY_MODIFIERS);
  1903. gtk_accel_group_connect(s->accel_group, GDK_KEY_equal, HOTKEY_MODIFIERS, 0,
  1904. g_cclosure_new_swap(G_CALLBACK(gd_accel_zoom_in), s, NULL));
  1905. gtk_menu_shell_append(GTK_MENU_SHELL(view_menu), s->zoom_in_item);
  1906. s->zoom_out_item = gtk_menu_item_new_with_mnemonic(_("Zoom _Out"));
  1907. gtk_menu_item_set_accel_path(GTK_MENU_ITEM(s->zoom_out_item),
  1908. "<QEMU>/View/Zoom Out");
  1909. gtk_accel_map_add_entry("<QEMU>/View/Zoom Out", GDK_KEY_minus,
  1910. HOTKEY_MODIFIERS);
  1911. gtk_menu_shell_append(GTK_MENU_SHELL(view_menu), s->zoom_out_item);
  1912. s->zoom_fixed_item = gtk_menu_item_new_with_mnemonic(_("Best _Fit"));
  1913. gtk_menu_item_set_accel_path(GTK_MENU_ITEM(s->zoom_fixed_item),
  1914. "<QEMU>/View/Zoom Fixed");
  1915. gtk_accel_map_add_entry("<QEMU>/View/Zoom Fixed", GDK_KEY_0,
  1916. HOTKEY_MODIFIERS);
  1917. gtk_menu_shell_append(GTK_MENU_SHELL(view_menu), s->zoom_fixed_item);
  1918. s->zoom_fit_item = gtk_check_menu_item_new_with_mnemonic(_("Zoom To _Fit"));
  1919. gtk_menu_shell_append(GTK_MENU_SHELL(view_menu), s->zoom_fit_item);
  1920. separator = gtk_separator_menu_item_new();
  1921. gtk_menu_shell_append(GTK_MENU_SHELL(view_menu), separator);
  1922. s->grab_on_hover_item = gtk_check_menu_item_new_with_mnemonic(_("Grab On _Hover"));
  1923. gtk_menu_shell_append(GTK_MENU_SHELL(view_menu), s->grab_on_hover_item);
  1924. s->grab_item = gtk_check_menu_item_new_with_mnemonic(_("_Grab Input"));
  1925. gtk_menu_item_set_accel_path(GTK_MENU_ITEM(s->grab_item),
  1926. "<QEMU>/View/Grab Input");
  1927. gtk_accel_map_add_entry("<QEMU>/View/Grab Input", GDK_KEY_g,
  1928. HOTKEY_MODIFIERS);
  1929. gtk_menu_shell_append(GTK_MENU_SHELL(view_menu), s->grab_item);
  1930. separator = gtk_separator_menu_item_new();
  1931. gtk_menu_shell_append(GTK_MENU_SHELL(view_menu), separator);
  1932. /* gfx */
  1933. for (vc = 0;; vc++) {
  1934. con = qemu_console_lookup_by_index(vc);
  1935. if (!con) {
  1936. break;
  1937. }
  1938. group = gd_vc_gfx_init(s, &s->vc[vc], con,
  1939. vc, group, view_menu);
  1940. s->nb_vcs++;
  1941. }
  1942. #if defined(CONFIG_VTE)
  1943. /* vte */
  1944. gd_vcs_init(s, group, view_menu);
  1945. #endif
  1946. separator = gtk_separator_menu_item_new();
  1947. gtk_menu_shell_append(GTK_MENU_SHELL(view_menu), separator);
  1948. s->show_tabs_item = gtk_check_menu_item_new_with_mnemonic(_("Show _Tabs"));
  1949. gtk_menu_shell_append(GTK_MENU_SHELL(view_menu), s->show_tabs_item);
  1950. s->untabify_item = gtk_menu_item_new_with_mnemonic(_("Detach Tab"));
  1951. gtk_menu_shell_append(GTK_MENU_SHELL(view_menu), s->untabify_item);
  1952. s->show_menubar_item = gtk_check_menu_item_new_with_mnemonic(
  1953. _("Show Menubar"));
  1954. gtk_check_menu_item_set_active(GTK_CHECK_MENU_ITEM(s->show_menubar_item),
  1955. TRUE);
  1956. gtk_accel_group_connect(s->accel_group, GDK_KEY_m, HOTKEY_MODIFIERS, 0,
  1957. g_cclosure_new_swap(G_CALLBACK(gd_accel_show_menubar), s, NULL));
  1958. #if GTK_CHECK_VERSION(3, 8, 0)
  1959. gtk_accel_label_set_accel(
  1960. GTK_ACCEL_LABEL(gtk_bin_get_child(GTK_BIN(s->show_menubar_item))),
  1961. GDK_KEY_m, HOTKEY_MODIFIERS);
  1962. #endif
  1963. gtk_menu_shell_append(GTK_MENU_SHELL(view_menu), s->show_menubar_item);
  1964. return view_menu;
  1965. }
  1966. static void gd_create_menus(GtkDisplayState *s)
  1967. {
  1968. GtkSettings *settings;
  1969. s->accel_group = gtk_accel_group_new();
  1970. s->machine_menu = gd_create_menu_machine(s);
  1971. s->view_menu = gd_create_menu_view(s);
  1972. s->machine_menu_item = gtk_menu_item_new_with_mnemonic(_("_Machine"));
  1973. gtk_menu_item_set_submenu(GTK_MENU_ITEM(s->machine_menu_item),
  1974. s->machine_menu);
  1975. gtk_menu_shell_append(GTK_MENU_SHELL(s->menu_bar), s->machine_menu_item);
  1976. s->view_menu_item = gtk_menu_item_new_with_mnemonic(_("_View"));
  1977. gtk_menu_item_set_submenu(GTK_MENU_ITEM(s->view_menu_item), s->view_menu);
  1978. gtk_menu_shell_append(GTK_MENU_SHELL(s->menu_bar), s->view_menu_item);
  1979. g_object_set_data(G_OBJECT(s->window), "accel_group", s->accel_group);
  1980. gtk_window_add_accel_group(GTK_WINDOW(s->window), s->accel_group);
  1981. /* Disable the default "F10" menu shortcut. */
  1982. settings = gtk_widget_get_settings(s->window);
  1983. g_object_set(G_OBJECT(settings), "gtk-menu-bar-accel", "", NULL);
  1984. }
  1985. static gboolean gtkinit;
  1986. static void gtk_display_init(DisplayState *ds, DisplayOptions *opts)
  1987. {
  1988. VirtualConsole *vc;
  1989. GtkDisplayState *s = g_malloc0(sizeof(*s));
  1990. char *filename;
  1991. GdkDisplay *window_display;
  1992. if (!gtkinit) {
  1993. fprintf(stderr, "gtk initialization failed\n");
  1994. exit(1);
  1995. }
  1996. assert(opts->type == DISPLAY_TYPE_GTK);
  1997. s->opts = opts;
  1998. #if !GTK_CHECK_VERSION(3, 0, 0)
  1999. g_printerr("Running QEMU with GTK 2.x is deprecated, and will be removed\n"
  2000. "in a future release. Please switch to GTK 3.x instead\n");
  2001. #endif
  2002. s->window = gtk_window_new(GTK_WINDOW_TOPLEVEL);
  2003. #if GTK_CHECK_VERSION(3, 2, 0)
  2004. s->vbox = gtk_box_new(GTK_ORIENTATION_VERTICAL, 0);
  2005. #else
  2006. s->vbox = gtk_vbox_new(FALSE, 0);
  2007. #endif
  2008. s->notebook = gtk_notebook_new();
  2009. s->menu_bar = gtk_menu_bar_new();
  2010. s->free_scale = FALSE;
  2011. /* Mostly LC_MESSAGES only. See early_gtk_display_init() for details. For
  2012. * LC_CTYPE, we need to make sure that non-ASCII characters are considered
  2013. * printable, but without changing any of the character classes to make
  2014. * sure that we don't accidentally break implicit assumptions. */
  2015. setlocale(LC_MESSAGES, "");
  2016. setlocale(LC_CTYPE, "C.UTF-8");
  2017. bindtextdomain("qemu", CONFIG_QEMU_LOCALEDIR);
  2018. textdomain("qemu");
  2019. window_display = gtk_widget_get_display(s->window);
  2020. s->null_cursor = gdk_cursor_new_for_display(window_display,
  2021. GDK_BLANK_CURSOR);
  2022. s->mouse_mode_notifier.notify = gd_mouse_mode_change;
  2023. qemu_add_mouse_mode_change_notifier(&s->mouse_mode_notifier);
  2024. qemu_add_vm_change_state_handler(gd_change_runstate, s);
  2025. filename = qemu_find_file(QEMU_FILE_TYPE_BIOS, "qemu_logo_no_text.svg");
  2026. if (filename) {
  2027. GError *error = NULL;
  2028. GdkPixbuf *pixbuf = gdk_pixbuf_new_from_file(filename, &error);
  2029. if (pixbuf) {
  2030. gtk_window_set_icon(GTK_WINDOW(s->window), pixbuf);
  2031. } else {
  2032. g_error_free(error);
  2033. }
  2034. g_free(filename);
  2035. }
  2036. gd_create_menus(s);
  2037. gd_connect_signals(s);
  2038. gtk_notebook_set_show_tabs(GTK_NOTEBOOK(s->notebook), FALSE);
  2039. gtk_notebook_set_show_border(GTK_NOTEBOOK(s->notebook), FALSE);
  2040. gd_update_caption(s);
  2041. gtk_box_pack_start(GTK_BOX(s->vbox), s->menu_bar, FALSE, TRUE, 0);
  2042. gtk_box_pack_start(GTK_BOX(s->vbox), s->notebook, TRUE, TRUE, 0);
  2043. gtk_container_add(GTK_CONTAINER(s->window), s->vbox);
  2044. gtk_widget_show_all(s->window);
  2045. #ifdef VTE_RESIZE_HACK
  2046. {
  2047. VirtualConsole *cur = gd_vc_find_current(s);
  2048. if (cur) {
  2049. int i;
  2050. for (i = 0; i < s->nb_vcs; i++) {
  2051. VirtualConsole *vc = &s->vc[i];
  2052. if (vc && vc->type == GD_VC_VTE && vc != cur) {
  2053. gtk_widget_hide(vc->vte.terminal);
  2054. }
  2055. }
  2056. gd_update_windowsize(cur);
  2057. }
  2058. }
  2059. #endif
  2060. vc = gd_vc_find_current(s);
  2061. gtk_widget_set_sensitive(s->view_menu, vc != NULL);
  2062. #ifdef CONFIG_VTE
  2063. gtk_widget_set_sensitive(s->copy_item,
  2064. vc && vc->type == GD_VC_VTE);
  2065. #endif
  2066. if (opts->has_full_screen &&
  2067. opts->full_screen) {
  2068. gtk_menu_item_activate(GTK_MENU_ITEM(s->full_screen_item));
  2069. }
  2070. if (opts->u.gtk.has_grab_on_hover &&
  2071. opts->u.gtk.grab_on_hover) {
  2072. gtk_menu_item_activate(GTK_MENU_ITEM(s->grab_on_hover_item));
  2073. }
  2074. }
  2075. static void early_gtk_display_init(DisplayOptions *opts)
  2076. {
  2077. /* The QEMU code relies on the assumption that it's always run in
  2078. * the C locale. Therefore it is not prepared to deal with
  2079. * operations that produce different results depending on the
  2080. * locale, such as printf's formatting of decimal numbers, and
  2081. * possibly others.
  2082. *
  2083. * Since GTK+ calls setlocale() by default -importing the locale
  2084. * settings from the environment- we must prevent it from doing so
  2085. * using gtk_disable_setlocale().
  2086. *
  2087. * QEMU's GTK+ UI, however, _does_ have translations for some of
  2088. * the menu items. As a trade-off between a functionally correct
  2089. * QEMU and a fully internationalized UI we support importing
  2090. * LC_MESSAGES from the environment (see the setlocale() call
  2091. * earlier in this file). This allows us to display translated
  2092. * messages leaving everything else untouched.
  2093. */
  2094. gtk_disable_setlocale();
  2095. gtkinit = gtk_init_check(NULL, NULL);
  2096. if (!gtkinit) {
  2097. /* don't exit yet, that'll break -help */
  2098. return;
  2099. }
  2100. assert(opts->type == DISPLAY_TYPE_GTK);
  2101. if (opts->has_gl && opts->gl != DISPLAYGL_MODE_OFF) {
  2102. if (opts->gl == DISPLAYGL_MODE_ES) {
  2103. error_report("gtk: opengl es not supported");
  2104. return;
  2105. }
  2106. #if defined(CONFIG_OPENGL)
  2107. #if defined(CONFIG_GTK_GL) && defined(GDK_WINDOWING_WAYLAND)
  2108. if (GDK_IS_WAYLAND_DISPLAY(gdk_display_get_default())) {
  2109. gtk_use_gl_area = true;
  2110. gtk_gl_area_init();
  2111. } else
  2112. #endif
  2113. {
  2114. gtk_egl_init();
  2115. }
  2116. #endif
  2117. }
  2118. keycode_map = gd_get_keymap(&keycode_maplen);
  2119. #if defined(CONFIG_VTE)
  2120. type_register(&char_gd_vc_type_info);
  2121. #endif
  2122. }
  2123. static QemuDisplay qemu_display_gtk = {
  2124. .type = DISPLAY_TYPE_GTK,
  2125. .early_init = early_gtk_display_init,
  2126. .init = gtk_display_init,
  2127. };
  2128. static void register_gtk(void)
  2129. {
  2130. qemu_display_register(&qemu_display_gtk);
  2131. }
  2132. type_init(register_gtk);