You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

пре 10 година
пре 10 година
пре 10 година
пре 10 година
пре 7 година
пре 10 година
пре 10 година
пре 10 година
пре 10 година
пре 10 година
пре 10 година
пре 10 година
пре 10 година
пре 10 година
пре 10 година
пре 10 година
пре 10 година
пре 10 година
пре 10 година
пре 10 година
пре 10 година
пре 10 година
пре 10 година
пре 10 година
пре 10 година
пре 10 година
пре 10 година
пре 10 година
пре 10 година
пре 9 година
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254125512561257125812591260126112621263126412651266126712681269127012711272127312741275127612771278127912801281128212831284128512861287128812891290129112921293129412951296129712981299130013011302130313041305130613071308130913101311131213131314131513161317131813191320132113221323132413251326132713281329133013311332133313341335133613371338133913401341134213431344134513461347134813491350135113521353135413551356135713581359136013611362136313641365136613671368136913701371137213731374137513761377137813791380138113821383138413851386138713881389139013911392139313941395139613971398139914001401
  1. /* Copyright (C) 2002-2005 RealVNC Ltd. All Rights Reserved.
  2. * Copyright 2011-2019 Pierre Ossman for Cendio AB
  3. *
  4. * This is free software; you can redistribute it and/or modify
  5. * it under the terms of the GNU General Public License as published by
  6. * the Free Software Foundation; either version 2 of the License, or
  7. * (at your option) any later version.
  8. *
  9. * This software is distributed in the hope that it will be useful,
  10. * but WITHOUT ANY WARRANTY; without even the implied warranty of
  11. * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  12. * GNU General Public License for more details.
  13. *
  14. * You should have received a copy of the GNU General Public License
  15. * along with this software; if not, write to the Free Software
  16. * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307,
  17. * USA.
  18. */
  19. #ifdef HAVE_CONFIG_H
  20. #include <config.h>
  21. #endif
  22. #include <assert.h>
  23. #include <stdio.h>
  24. #include <string.h>
  25. #include <rfb/CMsgWriter.h>
  26. #include <rfb/LogWriter.h>
  27. #include <rfb/Exception.h>
  28. #include <rfb/ledStates.h>
  29. #include <rfb/util.h>
  30. // FLTK can pull in the X11 headers on some systems
  31. #ifndef XK_VoidSymbol
  32. #define XK_LATIN1
  33. #define XK_MISCELLANY
  34. #define XK_XKB_KEYS
  35. #include <rfb/keysymdef.h>
  36. #endif
  37. #ifndef XF86XK_ModeLock
  38. #include <rfb/XF86keysym.h>
  39. #endif
  40. #if ! (defined(WIN32) || defined(__APPLE__))
  41. #include <X11/XKBlib.h>
  42. #endif
  43. #ifndef NoSymbol
  44. #define NoSymbol 0
  45. #endif
  46. // Missing in at least some versions of MinGW
  47. #ifndef MAPVK_VK_TO_VSC
  48. #define MAPVK_VK_TO_VSC 0
  49. #endif
  50. #include "fltk/layout.h"
  51. #include "fltk/util.h"
  52. #include "Viewport.h"
  53. #include "CConn.h"
  54. #include "OptionsDialog.h"
  55. #include "DesktopWindow.h"
  56. #include "i18n.h"
  57. #include "parameters.h"
  58. #include "keysym2ucs.h"
  59. #include "menukey.h"
  60. #include "vncviewer.h"
  61. #include "PlatformPixelBuffer.h"
  62. #include <FL/fl_draw.H>
  63. #include <FL/fl_ask.H>
  64. #include <FL/Fl_Menu.H>
  65. #include <FL/Fl_Menu_Button.H>
  66. #include <FL/x.H>
  67. #if !defined(WIN32) && !defined(__APPLE__)
  68. #include <X11/XKBlib.h>
  69. extern const struct _code_map_xkb_to_qnum {
  70. const char * from;
  71. const unsigned short to;
  72. } code_map_xkb_to_qnum[];
  73. extern const unsigned int code_map_xkb_to_qnum_len;
  74. static int code_map_keycode_to_qnum[256];
  75. #endif
  76. #ifdef __APPLE__
  77. #include "cocoa.h"
  78. extern const unsigned short code_map_osx_to_qnum[];
  79. extern const unsigned int code_map_osx_to_qnum_len;
  80. #endif
  81. #ifdef WIN32
  82. #include "win32.h"
  83. #endif
  84. using namespace rfb;
  85. static rfb::LogWriter vlog("Viewport");
  86. // Menu constants
  87. enum { ID_DISCONNECT, ID_FULLSCREEN, ID_MINIMIZE, ID_RESIZE,
  88. ID_CTRL, ID_ALT, ID_MENUKEY, ID_CTRLALTDEL,
  89. ID_REFRESH, ID_OPTIONS, ID_INFO, ID_ABOUT };
  90. // Used to detect fake input (0xaa is not a real key)
  91. #ifdef WIN32
  92. static const WORD SCAN_FAKE = 0xaa;
  93. #endif
  94. Viewport::Viewport(int w, int h, const rfb::PixelFormat& /*serverPF*/, CConn* cc_)
  95. : Fl_Widget(0, 0, w, h), cc(cc_), frameBuffer(NULL),
  96. lastPointerPos(0, 0), lastButtonMask(0),
  97. #ifdef WIN32
  98. altGrArmed(false),
  99. #endif
  100. firstLEDState(true),
  101. pendingServerClipboard(false), pendingClientClipboard(false),
  102. menuCtrlKey(false), menuAltKey(false), cursor(NULL)
  103. {
  104. #if !defined(WIN32) && !defined(__APPLE__)
  105. XkbDescPtr xkb;
  106. Status status;
  107. xkb = XkbGetMap(fl_display, 0, XkbUseCoreKbd);
  108. if (!xkb)
  109. throw rfb::Exception("XkbGetMap");
  110. status = XkbGetNames(fl_display, XkbKeyNamesMask, xkb);
  111. if (status != Success)
  112. throw rfb::Exception("XkbGetNames");
  113. memset(code_map_keycode_to_qnum, 0, sizeof(code_map_keycode_to_qnum));
  114. for (KeyCode keycode = xkb->min_key_code;
  115. keycode < xkb->max_key_code;
  116. keycode++) {
  117. const char *keyname = xkb->names->keys[keycode].name;
  118. unsigned short rfbcode;
  119. if (keyname[0] == '\0')
  120. continue;
  121. rfbcode = 0;
  122. for (unsigned i = 0;i < code_map_xkb_to_qnum_len;i++) {
  123. if (strncmp(code_map_xkb_to_qnum[i].from,
  124. keyname, XkbKeyNameLength) == 0) {
  125. rfbcode = code_map_xkb_to_qnum[i].to;
  126. break;
  127. }
  128. }
  129. if (rfbcode != 0)
  130. code_map_keycode_to_qnum[keycode] = rfbcode;
  131. else
  132. vlog.debug("No key mapping for key %.4s", keyname);
  133. }
  134. XkbFreeKeyboard(xkb, 0, True);
  135. #endif
  136. Fl::add_clipboard_notify(handleClipboardChange, this);
  137. // We need to intercept keyboard events early
  138. Fl::add_system_handler(handleSystemEvent, this);
  139. frameBuffer = new PlatformPixelBuffer(w, h);
  140. assert(frameBuffer);
  141. cc->setFramebuffer(frameBuffer);
  142. contextMenu = new Fl_Menu_Button(0, 0, 0, 0);
  143. // Setting box type to FL_NO_BOX prevents it from trying to draw the
  144. // button component (which we don't want)
  145. contextMenu->box(FL_NO_BOX);
  146. // The (invisible) button associated with this widget can mess with
  147. // things like Fl_Scroll so we need to get rid of any parents.
  148. // Unfortunately that's not possible because of STR #2654, but
  149. // reparenting to the current window works for most cases.
  150. window()->add(contextMenu);
  151. setMenuKey();
  152. OptionsDialog::addCallback(handleOptions, this);
  153. // Make sure we have an initial blank cursor set
  154. setCursor(0, 0, rfb::Point(0, 0), NULL);
  155. }
  156. Viewport::~Viewport()
  157. {
  158. // Unregister all timeouts in case they get a change tro trigger
  159. // again later when this object is already gone.
  160. Fl::remove_timeout(handlePointerTimeout, this);
  161. #ifdef WIN32
  162. Fl::remove_timeout(handleAltGrTimeout, this);
  163. #endif
  164. Fl::remove_system_handler(handleSystemEvent);
  165. Fl::remove_clipboard_notify(handleClipboardChange);
  166. OptionsDialog::removeCallback(handleOptions);
  167. if (cursor) {
  168. if (!cursor->alloc_array)
  169. delete [] cursor->array;
  170. delete cursor;
  171. }
  172. // FLTK automatically deletes all child widgets, so we shouldn't touch
  173. // them ourselves here
  174. }
  175. const rfb::PixelFormat &Viewport::getPreferredPF()
  176. {
  177. return frameBuffer->getPF();
  178. }
  179. // Copy the areas of the framebuffer that have been changed (damaged)
  180. // to the displayed window.
  181. void Viewport::updateWindow()
  182. {
  183. Rect r;
  184. r = frameBuffer->getDamage();
  185. damage(FL_DAMAGE_USER1, r.tl.x + x(), r.tl.y + y(), r.width(), r.height());
  186. }
  187. static const char * dotcursor_xpm[] = {
  188. "5 5 2 1",
  189. ". c #000000",
  190. " c #FFFFFF",
  191. " ",
  192. " ... ",
  193. " ... ",
  194. " ... ",
  195. " "};
  196. void Viewport::setCursor(int width, int height, const Point& hotspot,
  197. const uint8_t* data)
  198. {
  199. int i;
  200. if (cursor) {
  201. if (!cursor->alloc_array)
  202. delete [] cursor->array;
  203. delete cursor;
  204. }
  205. for (i = 0; i < width*height; i++)
  206. if (data[i*4 + 3] != 0) break;
  207. if ((i == width*height) && dotWhenNoCursor) {
  208. vlog.debug("cursor is empty - using dot");
  209. Fl_Pixmap pxm(dotcursor_xpm);
  210. cursor = new Fl_RGB_Image(&pxm);
  211. cursorHotspot.x = cursorHotspot.y = 2;
  212. } else {
  213. if ((width == 0) || (height == 0)) {
  214. uint8_t *buffer = new uint8_t[4];
  215. memset(buffer, 0, 4);
  216. cursor = new Fl_RGB_Image(buffer, 1, 1, 4);
  217. cursorHotspot.x = cursorHotspot.y = 0;
  218. } else {
  219. uint8_t *buffer = new uint8_t[width * height * 4];
  220. memcpy(buffer, data, width * height * 4);
  221. cursor = new Fl_RGB_Image(buffer, width, height, 4);
  222. cursorHotspot = hotspot;
  223. }
  224. }
  225. if (Fl::belowmouse() == this)
  226. window()->cursor(cursor, cursorHotspot.x, cursorHotspot.y);
  227. }
  228. void Viewport::handleClipboardRequest()
  229. {
  230. Fl::paste(*this, clipboardSource);
  231. }
  232. void Viewport::handleClipboardAnnounce(bool available)
  233. {
  234. if (!acceptClipboard)
  235. return;
  236. if (!available) {
  237. vlog.debug("Clipboard is no longer available on server");
  238. pendingServerClipboard = false;
  239. return;
  240. }
  241. pendingClientClipboard = false;
  242. if (!hasFocus()) {
  243. vlog.debug("Got notification of new clipboard on server whilst not focused, will request data later");
  244. pendingServerClipboard = true;
  245. return;
  246. }
  247. vlog.debug("Got notification of new clipboard on server, requesting data");
  248. cc->requestClipboard();
  249. }
  250. void Viewport::handleClipboardData(const char* data)
  251. {
  252. size_t len;
  253. if (!hasFocus())
  254. return;
  255. len = strlen(data);
  256. vlog.debug("Got clipboard data (%d bytes)", (int)len);
  257. // RFB doesn't have separate selection and clipboard concepts, so we
  258. // dump the data into both variants.
  259. #if !defined(WIN32) && !defined(__APPLE__)
  260. if (setPrimary)
  261. Fl::copy(data, len, 0);
  262. #endif
  263. Fl::copy(data, len, 1);
  264. }
  265. void Viewport::setLEDState(unsigned int state)
  266. {
  267. vlog.debug("Got server LED state: 0x%08x", state);
  268. // The first message is just considered to be the server announcing
  269. // support for this extension. We will push our state to sync up the
  270. // server when we get focus. If we already have focus we need to push
  271. // it here though.
  272. if (firstLEDState) {
  273. firstLEDState = false;
  274. if (hasFocus())
  275. pushLEDState();
  276. return;
  277. }
  278. if (!hasFocus())
  279. return;
  280. #if defined(WIN32)
  281. INPUT input[6];
  282. UINT count;
  283. UINT ret;
  284. memset(input, 0, sizeof(input));
  285. count = 0;
  286. if (!!(state & ledCapsLock) != !!(GetKeyState(VK_CAPITAL) & 0x1)) {
  287. input[count].type = input[count+1].type = INPUT_KEYBOARD;
  288. input[count].ki.wVk = input[count+1].ki.wVk = VK_CAPITAL;
  289. input[count].ki.wScan = input[count+1].ki.wScan = SCAN_FAKE;
  290. input[count].ki.dwFlags = 0;
  291. input[count+1].ki.dwFlags = KEYEVENTF_KEYUP;
  292. count += 2;
  293. }
  294. if (!!(state & ledNumLock) != !!(GetKeyState(VK_NUMLOCK) & 0x1)) {
  295. input[count].type = input[count+1].type = INPUT_KEYBOARD;
  296. input[count].ki.wVk = input[count+1].ki.wVk = VK_NUMLOCK;
  297. input[count].ki.wScan = input[count+1].ki.wScan = SCAN_FAKE;
  298. input[count].ki.dwFlags = KEYEVENTF_EXTENDEDKEY;
  299. input[count+1].ki.dwFlags = KEYEVENTF_KEYUP | KEYEVENTF_EXTENDEDKEY;
  300. count += 2;
  301. }
  302. if (!!(state & ledScrollLock) != !!(GetKeyState(VK_SCROLL) & 0x1)) {
  303. input[count].type = input[count+1].type = INPUT_KEYBOARD;
  304. input[count].ki.wVk = input[count+1].ki.wVk = VK_SCROLL;
  305. input[count].ki.wScan = input[count+1].ki.wScan = SCAN_FAKE;
  306. input[count].ki.dwFlags = 0;
  307. input[count+1].ki.dwFlags = KEYEVENTF_KEYUP;
  308. count += 2;
  309. }
  310. if (count == 0)
  311. return;
  312. ret = SendInput(count, input, sizeof(*input));
  313. if (ret < count)
  314. vlog.error(_("Failed to update keyboard LED state: %lu"), GetLastError());
  315. #elif defined(__APPLE__)
  316. int ret;
  317. ret = cocoa_set_caps_lock_state(state & ledCapsLock);
  318. if (ret != 0) {
  319. vlog.error(_("Failed to update keyboard LED state: %d"), ret);
  320. return;
  321. }
  322. ret = cocoa_set_num_lock_state(state & ledNumLock);
  323. if (ret != 0) {
  324. vlog.error(_("Failed to update keyboard LED state: %d"), ret);
  325. return;
  326. }
  327. // No support for Scroll Lock //
  328. #else
  329. unsigned int affect, values;
  330. unsigned int mask;
  331. Bool ret;
  332. affect = values = 0;
  333. affect |= LockMask;
  334. if (state & ledCapsLock)
  335. values |= LockMask;
  336. mask = getModifierMask(XK_Num_Lock);
  337. affect |= mask;
  338. if (state & ledNumLock)
  339. values |= mask;
  340. mask = getModifierMask(XK_Scroll_Lock);
  341. affect |= mask;
  342. if (state & ledScrollLock)
  343. values |= mask;
  344. ret = XkbLockModifiers(fl_display, XkbUseCoreKbd, affect, values);
  345. if (!ret)
  346. vlog.error(_("Failed to update keyboard LED state"));
  347. #endif
  348. }
  349. void Viewport::pushLEDState()
  350. {
  351. unsigned int state;
  352. // Server support?
  353. if (cc->server.ledState() == ledUnknown)
  354. return;
  355. state = 0;
  356. #if defined(WIN32)
  357. if (GetKeyState(VK_CAPITAL) & 0x1)
  358. state |= ledCapsLock;
  359. if (GetKeyState(VK_NUMLOCK) & 0x1)
  360. state |= ledNumLock;
  361. if (GetKeyState(VK_SCROLL) & 0x1)
  362. state |= ledScrollLock;
  363. #elif defined(__APPLE__)
  364. int ret;
  365. bool on;
  366. ret = cocoa_get_caps_lock_state(&on);
  367. if (ret != 0) {
  368. vlog.error(_("Failed to get keyboard LED state: %d"), ret);
  369. return;
  370. }
  371. if (on)
  372. state |= ledCapsLock;
  373. ret = cocoa_get_num_lock_state(&on);
  374. if (ret != 0) {
  375. vlog.error(_("Failed to get keyboard LED state: %d"), ret);
  376. return;
  377. }
  378. if (on)
  379. state |= ledNumLock;
  380. // No support for Scroll Lock //
  381. state |= (cc->server.ledState() & ledScrollLock);
  382. #else
  383. unsigned int mask;
  384. Status status;
  385. XkbStateRec xkbState;
  386. status = XkbGetState(fl_display, XkbUseCoreKbd, &xkbState);
  387. if (status != Success) {
  388. vlog.error(_("Failed to get keyboard LED state: %d"), status);
  389. return;
  390. }
  391. if (xkbState.locked_mods & LockMask)
  392. state |= ledCapsLock;
  393. mask = getModifierMask(XK_Num_Lock);
  394. if (xkbState.locked_mods & mask)
  395. state |= ledNumLock;
  396. mask = getModifierMask(XK_Scroll_Lock);
  397. if (xkbState.locked_mods & mask)
  398. state |= ledScrollLock;
  399. #endif
  400. if ((state & ledCapsLock) != (cc->server.ledState() & ledCapsLock)) {
  401. vlog.debug("Inserting fake CapsLock to get in sync with server");
  402. handleKeyPress(0x3a, XK_Caps_Lock);
  403. handleKeyRelease(0x3a);
  404. }
  405. if ((state & ledNumLock) != (cc->server.ledState() & ledNumLock)) {
  406. vlog.debug("Inserting fake NumLock to get in sync with server");
  407. handleKeyPress(0x45, XK_Num_Lock);
  408. handleKeyRelease(0x45);
  409. }
  410. if ((state & ledScrollLock) != (cc->server.ledState() & ledScrollLock)) {
  411. vlog.debug("Inserting fake ScrollLock to get in sync with server");
  412. handleKeyPress(0x46, XK_Scroll_Lock);
  413. handleKeyRelease(0x46);
  414. }
  415. }
  416. void Viewport::draw(Surface* dst)
  417. {
  418. int X, Y, W, H;
  419. // Check what actually needs updating
  420. fl_clip_box(x(), y(), w(), h(), X, Y, W, H);
  421. if ((W == 0) || (H == 0))
  422. return;
  423. frameBuffer->draw(dst, X - x(), Y - y(), X, Y, W, H);
  424. }
  425. void Viewport::draw()
  426. {
  427. int X, Y, W, H;
  428. // Check what actually needs updating
  429. fl_clip_box(x(), y(), w(), h(), X, Y, W, H);
  430. if ((W == 0) || (H == 0))
  431. return;
  432. frameBuffer->draw(X - x(), Y - y(), X, Y, W, H);
  433. }
  434. void Viewport::resize(int x, int y, int w, int h)
  435. {
  436. if ((w != frameBuffer->width()) || (h != frameBuffer->height())) {
  437. vlog.debug("Resizing framebuffer from %dx%d to %dx%d",
  438. frameBuffer->width(), frameBuffer->height(), w, h);
  439. frameBuffer = new PlatformPixelBuffer(w, h);
  440. assert(frameBuffer);
  441. cc->setFramebuffer(frameBuffer);
  442. }
  443. Fl_Widget::resize(x, y, w, h);
  444. }
  445. int Viewport::handle(int event)
  446. {
  447. std::string filtered;
  448. int buttonMask, wheelMask;
  449. DownMap::const_iterator iter;
  450. switch (event) {
  451. case FL_PASTE:
  452. if (!isValidUTF8(Fl::event_text(), Fl::event_length())) {
  453. vlog.error("Invalid UTF-8 sequence in system clipboard");
  454. return 1;
  455. }
  456. filtered = convertLF(Fl::event_text(), Fl::event_length());
  457. vlog.debug("Sending clipboard data (%d bytes)", (int)filtered.size());
  458. try {
  459. cc->sendClipboardData(filtered.c_str());
  460. } catch (rdr::Exception& e) {
  461. vlog.error("%s", e.str());
  462. abort_connection_with_unexpected_error(e);
  463. }
  464. return 1;
  465. case FL_ENTER:
  466. window()->cursor(cursor, cursorHotspot.x, cursorHotspot.y);
  467. // Yes, we would like some pointer events please!
  468. return 1;
  469. case FL_LEAVE:
  470. window()->cursor(FL_CURSOR_DEFAULT);
  471. // We want a last move event to help trigger edge stuff
  472. handlePointerEvent(Point(Fl::event_x() - x(), Fl::event_y() - y()), 0);
  473. return 1;
  474. case FL_PUSH:
  475. case FL_RELEASE:
  476. case FL_DRAG:
  477. case FL_MOVE:
  478. case FL_MOUSEWHEEL:
  479. buttonMask = 0;
  480. if (Fl::event_button1())
  481. buttonMask |= 1;
  482. if (Fl::event_button2())
  483. buttonMask |= 2;
  484. if (Fl::event_button3())
  485. buttonMask |= 4;
  486. if (event == FL_MOUSEWHEEL) {
  487. wheelMask = 0;
  488. if (Fl::event_dy() < 0)
  489. wheelMask |= 8;
  490. if (Fl::event_dy() > 0)
  491. wheelMask |= 16;
  492. if (Fl::event_dx() < 0)
  493. wheelMask |= 32;
  494. if (Fl::event_dx() > 0)
  495. wheelMask |= 64;
  496. // A quick press of the wheel "button", followed by a immediate
  497. // release below
  498. handlePointerEvent(Point(Fl::event_x() - x(), Fl::event_y() - y()),
  499. buttonMask | wheelMask);
  500. }
  501. handlePointerEvent(Point(Fl::event_x() - x(), Fl::event_y() - y()), buttonMask);
  502. return 1;
  503. case FL_FOCUS:
  504. Fl::disable_im();
  505. flushPendingClipboard();
  506. // We may have gotten our lock keys out of sync with the server
  507. // whilst we didn't have focus. Try to sort this out.
  508. pushLEDState();
  509. // Resend Ctrl/Alt if needed
  510. if (menuCtrlKey)
  511. handleKeyPress(0x1d, XK_Control_L);
  512. if (menuAltKey)
  513. handleKeyPress(0x38, XK_Alt_L);
  514. // Yes, we would like some focus please!
  515. return 1;
  516. case FL_UNFOCUS:
  517. // We won't get more key events, so reset our knowledge about keys
  518. resetKeyboard();
  519. Fl::enable_im();
  520. return 1;
  521. case FL_KEYDOWN:
  522. case FL_KEYUP:
  523. // Just ignore these as keys were handled in the event handler
  524. return 1;
  525. }
  526. return Fl_Widget::handle(event);
  527. }
  528. void Viewport::sendPointerEvent(const rfb::Point& pos, int buttonMask)
  529. {
  530. if (viewOnly)
  531. return;
  532. if ((pointerEventInterval == 0) || (buttonMask != lastButtonMask)) {
  533. try {
  534. cc->writer()->writePointerEvent(pos, buttonMask);
  535. } catch (rdr::Exception& e) {
  536. vlog.error("%s", e.str());
  537. abort_connection_with_unexpected_error(e);
  538. }
  539. } else {
  540. if (!Fl::has_timeout(handlePointerTimeout, this))
  541. Fl::add_timeout((double)pointerEventInterval/1000.0,
  542. handlePointerTimeout, this);
  543. }
  544. lastPointerPos = pos;
  545. lastButtonMask = buttonMask;
  546. }
  547. bool Viewport::hasFocus()
  548. {
  549. Fl_Widget* focus;
  550. focus = Fl::grab();
  551. if (!focus)
  552. focus = Fl::focus();
  553. return focus == this;
  554. }
  555. #if ! (defined(WIN32) || defined(__APPLE__))
  556. unsigned int Viewport::getModifierMask(unsigned int keysym)
  557. {
  558. XkbDescPtr xkb;
  559. unsigned int mask, keycode;
  560. XkbAction *act;
  561. mask = 0;
  562. xkb = XkbGetMap(fl_display, XkbAllComponentsMask, XkbUseCoreKbd);
  563. if (xkb == NULL)
  564. return 0;
  565. for (keycode = xkb->min_key_code; keycode <= xkb->max_key_code; keycode++) {
  566. unsigned int state_out;
  567. KeySym ks;
  568. XkbTranslateKeyCode(xkb, keycode, 0, &state_out, &ks);
  569. if (ks == NoSymbol)
  570. continue;
  571. if (ks == keysym)
  572. break;
  573. }
  574. // KeySym not mapped?
  575. if (keycode > xkb->max_key_code)
  576. goto out;
  577. act = XkbKeyAction(xkb, keycode, 0);
  578. if (act == NULL)
  579. goto out;
  580. if (act->type != XkbSA_LockMods)
  581. goto out;
  582. if (act->mods.flags & XkbSA_UseModMapMods)
  583. mask = xkb->map->modmap[keycode];
  584. else
  585. mask = act->mods.mask;
  586. out:
  587. XkbFreeKeyboard(xkb, XkbAllComponentsMask, True);
  588. return mask;
  589. }
  590. #endif
  591. void Viewport::handleClipboardChange(int source, void *data)
  592. {
  593. Viewport *self = (Viewport *)data;
  594. assert(self);
  595. if (!sendClipboard)
  596. return;
  597. #if !defined(WIN32) && !defined(__APPLE__)
  598. if (!sendPrimary && (source == 0))
  599. return;
  600. #endif
  601. self->clipboardSource = source;
  602. self->pendingServerClipboard = false;
  603. if (!self->hasFocus()) {
  604. vlog.debug("Local clipboard changed whilst not focused, will notify server later");
  605. self->pendingClientClipboard = true;
  606. // Clear any older client clipboard from the server
  607. self->cc->announceClipboard(false);
  608. return;
  609. }
  610. vlog.debug("Local clipboard changed, notifying server");
  611. try {
  612. self->cc->announceClipboard(true);
  613. } catch (rdr::Exception& e) {
  614. vlog.error("%s", e.str());
  615. abort_connection_with_unexpected_error(e);
  616. }
  617. }
  618. void Viewport::flushPendingClipboard()
  619. {
  620. if (pendingServerClipboard) {
  621. vlog.debug("Focus regained after remote clipboard change, requesting data");
  622. try {
  623. cc->requestClipboard();
  624. } catch (rdr::Exception& e) {
  625. vlog.error("%s", e.str());
  626. abort_connection_with_unexpected_error(e);
  627. }
  628. }
  629. if (pendingClientClipboard) {
  630. vlog.debug("Focus regained after local clipboard change, notifying server");
  631. try {
  632. cc->announceClipboard(true);
  633. } catch (rdr::Exception& e) {
  634. vlog.error("%s", e.str());
  635. abort_connection_with_unexpected_error(e);
  636. }
  637. }
  638. pendingServerClipboard = false;
  639. pendingClientClipboard = false;
  640. }
  641. void Viewport::handlePointerEvent(const rfb::Point& pos, int buttonMask)
  642. {
  643. filterPointerEvent(pos, buttonMask);
  644. }
  645. void Viewport::handlePointerTimeout(void *data)
  646. {
  647. Viewport *self = (Viewport *)data;
  648. assert(self);
  649. try {
  650. self->cc->writer()->writePointerEvent(self->lastPointerPos,
  651. self->lastButtonMask);
  652. } catch (rdr::Exception& e) {
  653. vlog.error("%s", e.str());
  654. abort_connection_with_unexpected_error(e);
  655. }
  656. }
  657. void Viewport::resetKeyboard()
  658. {
  659. while (!downKeySym.empty())
  660. handleKeyRelease(downKeySym.begin()->first);
  661. }
  662. void Viewport::handleKeyPress(int keyCode, uint32_t keySym)
  663. {
  664. static bool menuRecursion = false;
  665. // Prevent recursion if the menu wants to send its own
  666. // activation key.
  667. if (menuKeySym && (keySym == menuKeySym) && !menuRecursion) {
  668. menuRecursion = true;
  669. popupContextMenu();
  670. menuRecursion = false;
  671. return;
  672. }
  673. if (viewOnly)
  674. return;
  675. if (keyCode == 0) {
  676. vlog.error(_("No key code specified on key press"));
  677. return;
  678. }
  679. #ifdef __APPLE__
  680. // Alt on OS X behaves more like AltGr on other systems, and to get
  681. // sane behaviour we should translate things in that manner for the
  682. // remote VNC server. However that means we lose the ability to use
  683. // Alt as a shortcut modifier. Do what RealVNC does and hijack the
  684. // left command key as an Alt replacement.
  685. switch (keySym) {
  686. case XK_Super_L:
  687. keySym = XK_Alt_L;
  688. break;
  689. case XK_Super_R:
  690. keySym = XK_Super_L;
  691. break;
  692. case XK_Alt_L:
  693. keySym = XK_Mode_switch;
  694. break;
  695. case XK_Alt_R:
  696. keySym = XK_ISO_Level3_Shift;
  697. break;
  698. }
  699. #endif
  700. // Because of the way keyboards work, we cannot expect to have the same
  701. // symbol on release as when pressed. This breaks the VNC protocol however,
  702. // so we need to keep track of what keysym a key _code_ generated on press
  703. // and send the same on release.
  704. downKeySym[keyCode] = keySym;
  705. #if defined(WIN32) || defined(__APPLE__)
  706. vlog.debug("Key pressed: 0x%04x => 0x%04x", keyCode, keySym);
  707. #else
  708. vlog.debug("Key pressed: 0x%04x => XK_%s (0x%04x)",
  709. keyCode, XKeysymToString(keySym), keySym);
  710. #endif
  711. try {
  712. // Fake keycode?
  713. if (keyCode > 0xff)
  714. cc->writer()->writeKeyEvent(keySym, 0, true);
  715. else
  716. cc->writer()->writeKeyEvent(keySym, keyCode, true);
  717. } catch (rdr::Exception& e) {
  718. vlog.error("%s", e.str());
  719. abort_connection_with_unexpected_error(e);
  720. }
  721. }
  722. void Viewport::handleKeyRelease(int keyCode)
  723. {
  724. DownMap::iterator iter;
  725. if (viewOnly)
  726. return;
  727. iter = downKeySym.find(keyCode);
  728. if (iter == downKeySym.end()) {
  729. // These occur somewhat frequently so let's not spam them unless
  730. // logging is turned up.
  731. vlog.debug("Unexpected release of key code %d", keyCode);
  732. return;
  733. }
  734. #if defined(WIN32) || defined(__APPLE__)
  735. vlog.debug("Key released: 0x%04x => 0x%04x", keyCode, iter->second);
  736. #else
  737. vlog.debug("Key released: 0x%04x => XK_%s (0x%04x)",
  738. keyCode, XKeysymToString(iter->second), iter->second);
  739. #endif
  740. try {
  741. if (keyCode > 0xff)
  742. cc->writer()->writeKeyEvent(iter->second, 0, false);
  743. else
  744. cc->writer()->writeKeyEvent(iter->second, keyCode, false);
  745. } catch (rdr::Exception& e) {
  746. vlog.error("%s", e.str());
  747. abort_connection_with_unexpected_error(e);
  748. }
  749. downKeySym.erase(iter);
  750. }
  751. int Viewport::handleSystemEvent(void *event, void *data)
  752. {
  753. Viewport *self = (Viewport *)data;
  754. assert(self);
  755. if (!self->hasFocus())
  756. return 0;
  757. assert(event);
  758. #if defined(WIN32)
  759. MSG *msg = (MSG*)event;
  760. if ((msg->message == WM_MOUSEMOVE) ||
  761. (msg->message == WM_LBUTTONDOWN) ||
  762. (msg->message == WM_LBUTTONUP) ||
  763. (msg->message == WM_RBUTTONDOWN) ||
  764. (msg->message == WM_RBUTTONUP) ||
  765. (msg->message == WM_MBUTTONDOWN) ||
  766. (msg->message == WM_MBUTTONUP) ||
  767. (msg->message == WM_MOUSEWHEEL) ||
  768. (msg->message == WM_MOUSEHWHEEL)) {
  769. // We can't get a mouse event in the middle of an AltGr sequence, so
  770. // abort that detection
  771. if (self->altGrArmed)
  772. self->resolveAltGrDetection(false);
  773. return 0; // We didn't really consume the mouse event
  774. } else if ((msg->message == WM_KEYDOWN) || (msg->message == WM_SYSKEYDOWN)) {
  775. UINT vKey;
  776. bool isExtended;
  777. int keyCode;
  778. uint32_t keySym;
  779. vKey = msg->wParam;
  780. isExtended = (msg->lParam & (1 << 24)) != 0;
  781. keyCode = ((msg->lParam >> 16) & 0xff);
  782. // Windows' touch keyboard doesn't set a scan code for the Alt
  783. // portion of the AltGr sequence, so we need to help it out
  784. if (!isExtended && (keyCode == 0x00) && (vKey == VK_MENU)) {
  785. isExtended = true;
  786. keyCode = 0x38;
  787. }
  788. // Windows doesn't have a proper AltGr, but handles it using fake
  789. // Ctrl+Alt. However the remote end might not be Windows, so we need
  790. // to merge those in to a single AltGr event. We detect this case
  791. // by seeing the two key events directly after each other with a very
  792. // short time between them (<50ms) and supress the Ctrl event.
  793. if (self->altGrArmed) {
  794. bool altPressed = isExtended &&
  795. (keyCode == 0x38) &&
  796. (vKey == VK_MENU) &&
  797. ((msg->time - self->altGrCtrlTime) < 50);
  798. self->resolveAltGrDetection(altPressed);
  799. }
  800. if (keyCode == SCAN_FAKE) {
  801. vlog.debug("Ignoring fake key press (virtual key 0x%02x)", vKey);
  802. return 1;
  803. }
  804. // Windows sets the scan code to 0x00 for multimedia keys, so we
  805. // have to do a reverse lookup based on the vKey.
  806. if (keyCode == 0x00) {
  807. keyCode = MapVirtualKey(vKey, MAPVK_VK_TO_VSC);
  808. if (keyCode == 0x00) {
  809. if (isExtended)
  810. vlog.error(_("No scan code for extended virtual key 0x%02x"), (int)vKey);
  811. else
  812. vlog.error(_("No scan code for virtual key 0x%02x"), (int)vKey);
  813. return 1;
  814. }
  815. }
  816. if (keyCode & ~0x7f) {
  817. vlog.error(_("Invalid scan code 0x%02x"), (int)keyCode);
  818. return 1;
  819. }
  820. if (isExtended)
  821. keyCode |= 0x80;
  822. // Fortunately RFB and Windows use the same scan code set (mostly),
  823. // so there is no conversion needed
  824. // (as long as we encode the extended keys with the high bit)
  825. // However Pause sends a code that conflicts with NumLock, so use
  826. // the code most RFB implementations use (part of the sequence for
  827. // Ctrl+Pause, i.e. Break)
  828. if (keyCode == 0x45)
  829. keyCode = 0xc6;
  830. // And NumLock incorrectly has the extended bit set
  831. if (keyCode == 0xc5)
  832. keyCode = 0x45;
  833. // And Alt+PrintScreen (i.e. SysRq) sends a different code than
  834. // PrintScreen
  835. if (keyCode == 0xb7)
  836. keyCode = 0x54;
  837. keySym = win32_vkey_to_keysym(vKey, isExtended);
  838. if (keySym == NoSymbol) {
  839. if (isExtended)
  840. vlog.error(_("No symbol for extended virtual key 0x%02x"), (int)vKey);
  841. else
  842. vlog.error(_("No symbol for virtual key 0x%02x"), (int)vKey);
  843. }
  844. // Windows sends the same vKey for both shifts, so we need to look
  845. // at the scan code to tell them apart
  846. if ((keySym == XK_Shift_L) && (keyCode == 0x36))
  847. keySym = XK_Shift_R;
  848. // AltGr handling (see above)
  849. if (win32_has_altgr()) {
  850. if ((keyCode == 0xb8) && (keySym == XK_Alt_R))
  851. keySym = XK_ISO_Level3_Shift;
  852. // Possible start of AltGr sequence?
  853. if ((keyCode == 0x1d) && (keySym == XK_Control_L)) {
  854. self->altGrArmed = true;
  855. self->altGrCtrlTime = msg->time;
  856. Fl::add_timeout(0.1, handleAltGrTimeout, self);
  857. return 1;
  858. }
  859. }
  860. self->handleKeyPress(keyCode, keySym);
  861. // We don't get reliable WM_KEYUP for these
  862. switch (keySym) {
  863. case XK_Zenkaku_Hankaku:
  864. case XK_Eisu_toggle:
  865. case XK_Katakana:
  866. case XK_Hiragana:
  867. case XK_Romaji:
  868. self->handleKeyRelease(keyCode);
  869. }
  870. return 1;
  871. } else if ((msg->message == WM_KEYUP) || (msg->message == WM_SYSKEYUP)) {
  872. UINT vKey;
  873. bool isExtended;
  874. int keyCode;
  875. vKey = msg->wParam;
  876. isExtended = (msg->lParam & (1 << 24)) != 0;
  877. keyCode = ((msg->lParam >> 16) & 0xff);
  878. // Touch keyboard AltGr (see above)
  879. if (!isExtended && (keyCode == 0x00) && (vKey == VK_MENU)) {
  880. isExtended = true;
  881. keyCode = 0x38;
  882. }
  883. // We can't get a release in the middle of an AltGr sequence, so
  884. // abort that detection
  885. if (self->altGrArmed)
  886. self->resolveAltGrDetection(false);
  887. if (keyCode == SCAN_FAKE) {
  888. vlog.debug("Ignoring fake key release (virtual key 0x%02x)", vKey);
  889. return 1;
  890. }
  891. if (keyCode == 0x00)
  892. keyCode = MapVirtualKey(vKey, MAPVK_VK_TO_VSC);
  893. if (isExtended)
  894. keyCode |= 0x80;
  895. if (keyCode == 0x45)
  896. keyCode = 0xc6;
  897. if (keyCode == 0xc5)
  898. keyCode = 0x45;
  899. if (keyCode == 0xb7)
  900. keyCode = 0x54;
  901. self->handleKeyRelease(keyCode);
  902. // Windows has a rather nasty bug where it won't send key release
  903. // events for a Shift button if the other Shift is still pressed
  904. if ((keyCode == 0x2a) || (keyCode == 0x36)) {
  905. if (self->downKeySym.count(0x2a))
  906. self->handleKeyRelease(0x2a);
  907. if (self->downKeySym.count(0x36))
  908. self->handleKeyRelease(0x36);
  909. }
  910. return 1;
  911. }
  912. #elif defined(__APPLE__)
  913. // Special event that means we temporarily lost some input
  914. if (cocoa_is_keyboard_sync(event)) {
  915. self->resetKeyboard();
  916. return 1;
  917. }
  918. if (cocoa_is_keyboard_event(event)) {
  919. int keyCode;
  920. keyCode = cocoa_event_keycode(event);
  921. if ((unsigned)keyCode >= code_map_osx_to_qnum_len)
  922. keyCode = 0;
  923. else
  924. keyCode = code_map_osx_to_qnum[keyCode];
  925. if (cocoa_is_key_press(event)) {
  926. uint32_t keySym;
  927. keySym = cocoa_event_keysym(event);
  928. if (keySym == NoSymbol) {
  929. vlog.error(_("No symbol for key code 0x%02x (in the current state)"),
  930. (int)keyCode);
  931. }
  932. self->handleKeyPress(keyCode, keySym);
  933. // We don't get any release events for CapsLock, so we have to
  934. // send the release right away.
  935. if (keySym == XK_Caps_Lock)
  936. self->handleKeyRelease(keyCode);
  937. } else {
  938. self->handleKeyRelease(keyCode);
  939. }
  940. return 1;
  941. }
  942. #else
  943. XEvent *xevent = (XEvent*)event;
  944. if (xevent->type == KeyPress) {
  945. int keycode;
  946. char str;
  947. KeySym keysym;
  948. keycode = code_map_keycode_to_qnum[xevent->xkey.keycode];
  949. // Generate a fake keycode just for tracking if we can't figure
  950. // out the proper one
  951. if (keycode == 0)
  952. keycode = 0x100 | xevent->xkey.keycode;
  953. XLookupString(&xevent->xkey, &str, 1, &keysym, NULL);
  954. if (keysym == NoSymbol) {
  955. vlog.error(_("No symbol for key code %d (in the current state)"),
  956. (int)xevent->xkey.keycode);
  957. }
  958. switch (keysym) {
  959. // For the first few years, there wasn't a good consensus on what the
  960. // Windows keys should be mapped to for X11. So we need to help out a
  961. // bit and map all variants to the same key...
  962. case XK_Hyper_L:
  963. keysym = XK_Super_L;
  964. break;
  965. case XK_Hyper_R:
  966. keysym = XK_Super_R;
  967. break;
  968. // There has been several variants for Shift-Tab over the years.
  969. // RFB states that we should always send a normal tab.
  970. case XK_ISO_Left_Tab:
  971. keysym = XK_Tab;
  972. break;
  973. }
  974. self->handleKeyPress(keycode, keysym);
  975. return 1;
  976. } else if (xevent->type == KeyRelease) {
  977. int keycode = code_map_keycode_to_qnum[xevent->xkey.keycode];
  978. if (keycode == 0)
  979. keycode = 0x100 | xevent->xkey.keycode;
  980. self->handleKeyRelease(keycode);
  981. return 1;
  982. }
  983. #endif
  984. return 0;
  985. }
  986. #ifdef WIN32
  987. void Viewport::handleAltGrTimeout(void *data)
  988. {
  989. Viewport *self = (Viewport *)data;
  990. assert(self);
  991. self->altGrArmed = false;
  992. self->handleKeyPress(0x1d, XK_Control_L);
  993. }
  994. void Viewport::resolveAltGrDetection(bool isAltGrSequence)
  995. {
  996. altGrArmed = false;
  997. Fl::remove_timeout(handleAltGrTimeout);
  998. // when it's not an AltGr sequence we can't supress the Ctrl anymore
  999. if (!isAltGrSequence)
  1000. handleKeyPress(0x1d, XK_Control_L);
  1001. }
  1002. #endif
  1003. void Viewport::initContextMenu()
  1004. {
  1005. contextMenu->clear();
  1006. fltk_menu_add(contextMenu, p_("ContextMenu|", "Dis&connect"),
  1007. 0, NULL, (void*)ID_DISCONNECT, FL_MENU_DIVIDER);
  1008. fltk_menu_add(contextMenu, p_("ContextMenu|", "&Full screen"),
  1009. 0, NULL, (void*)ID_FULLSCREEN,
  1010. FL_MENU_TOGGLE | (window()->fullscreen_active()?FL_MENU_VALUE:0));
  1011. fltk_menu_add(contextMenu, p_("ContextMenu|", "Minimi&ze"),
  1012. 0, NULL, (void*)ID_MINIMIZE, 0);
  1013. fltk_menu_add(contextMenu, p_("ContextMenu|", "Resize &window to session"),
  1014. 0, NULL, (void*)ID_RESIZE,
  1015. (window()->fullscreen_active()?FL_MENU_INACTIVE:0) |
  1016. FL_MENU_DIVIDER);
  1017. fltk_menu_add(contextMenu, p_("ContextMenu|", "&Ctrl"),
  1018. 0, NULL, (void*)ID_CTRL,
  1019. FL_MENU_TOGGLE | (menuCtrlKey?FL_MENU_VALUE:0));
  1020. fltk_menu_add(contextMenu, p_("ContextMenu|", "&Alt"),
  1021. 0, NULL, (void*)ID_ALT,
  1022. FL_MENU_TOGGLE | (menuAltKey?FL_MENU_VALUE:0));
  1023. if (menuKeySym) {
  1024. char sendMenuKey[64];
  1025. snprintf(sendMenuKey, 64, p_("ContextMenu|", "Send %s"), (const char *)menuKey);
  1026. fltk_menu_add(contextMenu, sendMenuKey, 0, NULL, (void*)ID_MENUKEY, 0);
  1027. fltk_menu_add(contextMenu, "Secret shortcut menu key", menuKeyFLTK, NULL,
  1028. (void*)ID_MENUKEY, FL_MENU_INVISIBLE);
  1029. }
  1030. fltk_menu_add(contextMenu, p_("ContextMenu|", "Send Ctrl-Alt-&Del"),
  1031. 0, NULL, (void*)ID_CTRLALTDEL, FL_MENU_DIVIDER);
  1032. fltk_menu_add(contextMenu, p_("ContextMenu|", "&Refresh screen"),
  1033. 0, NULL, (void*)ID_REFRESH, FL_MENU_DIVIDER);
  1034. fltk_menu_add(contextMenu, p_("ContextMenu|", "&Options..."),
  1035. 0, NULL, (void*)ID_OPTIONS, 0);
  1036. fltk_menu_add(contextMenu, p_("ContextMenu|", "Connection &info..."),
  1037. 0, NULL, (void*)ID_INFO, 0);
  1038. fltk_menu_add(contextMenu, p_("ContextMenu|", "About &TigerVNC viewer..."),
  1039. 0, NULL, (void*)ID_ABOUT, 0);
  1040. }
  1041. void Viewport::popupContextMenu()
  1042. {
  1043. const Fl_Menu_Item *m;
  1044. char buffer[1024];
  1045. // Make sure the menu is reset to its initial state between goes or
  1046. // it will start up highlighting the previously selected entry.
  1047. contextMenu->value(-1);
  1048. // initialize context menu before display
  1049. initContextMenu();
  1050. // Unfortunately FLTK doesn't reliably restore the mouse pointer for
  1051. // menus, so we have to help it out.
  1052. if (Fl::belowmouse() == this)
  1053. window()->cursor(FL_CURSOR_DEFAULT);
  1054. // FLTK also doesn't switch focus properly for menus
  1055. handle(FL_UNFOCUS);
  1056. m = contextMenu->popup();
  1057. handle(FL_FOCUS);
  1058. // Back to our proper mouse pointer.
  1059. if (Fl::belowmouse())
  1060. window()->cursor(cursor, cursorHotspot.x, cursorHotspot.y);
  1061. if (m == NULL)
  1062. return;
  1063. switch (m->argument()) {
  1064. case ID_DISCONNECT:
  1065. disconnect();
  1066. break;
  1067. case ID_FULLSCREEN:
  1068. if (window()->fullscreen_active())
  1069. window()->fullscreen_off();
  1070. else
  1071. ((DesktopWindow*)window())->fullscreen_on();
  1072. break;
  1073. case ID_MINIMIZE:
  1074. window()->iconize();
  1075. break;
  1076. case ID_RESIZE:
  1077. if (window()->fullscreen_active())
  1078. break;
  1079. window()->size(w(), h());
  1080. break;
  1081. case ID_CTRL:
  1082. if (m->value())
  1083. handleKeyPress(0x1d, XK_Control_L);
  1084. else
  1085. handleKeyRelease(0x1d);
  1086. menuCtrlKey = !menuCtrlKey;
  1087. break;
  1088. case ID_ALT:
  1089. if (m->value())
  1090. handleKeyPress(0x38, XK_Alt_L);
  1091. else
  1092. handleKeyRelease(0x38);
  1093. menuAltKey = !menuAltKey;
  1094. break;
  1095. case ID_MENUKEY:
  1096. handleKeyPress(menuKeyCode, menuKeySym);
  1097. handleKeyRelease(menuKeyCode);
  1098. break;
  1099. case ID_CTRLALTDEL:
  1100. handleKeyPress(0x1d, XK_Control_L);
  1101. handleKeyPress(0x38, XK_Alt_L);
  1102. handleKeyPress(0xd3, XK_Delete);
  1103. handleKeyRelease(0xd3);
  1104. handleKeyRelease(0x38);
  1105. handleKeyRelease(0x1d);
  1106. break;
  1107. case ID_REFRESH:
  1108. cc->refreshFramebuffer();
  1109. break;
  1110. case ID_OPTIONS:
  1111. OptionsDialog::showDialog();
  1112. break;
  1113. case ID_INFO:
  1114. if (fltk_escape(cc->connectionInfo(), buffer, sizeof(buffer)) < sizeof(buffer)) {
  1115. fl_message_title(_("VNC connection info"));
  1116. fl_message("%s", buffer);
  1117. }
  1118. break;
  1119. case ID_ABOUT:
  1120. about_vncviewer();
  1121. break;
  1122. }
  1123. }
  1124. void Viewport::setMenuKey()
  1125. {
  1126. getMenuKey(&menuKeyFLTK, &menuKeyCode, &menuKeySym);
  1127. }
  1128. void Viewport::handleOptions(void *data)
  1129. {
  1130. Viewport *self = (Viewport*)data;
  1131. self->setMenuKey();
  1132. // FIXME: Need to recheck cursor for dotWhenNoCursor
  1133. }