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.

OptionsDialog.cxx 35KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170
  1. /* Copyright 2011-2021 Pierre Ossman <ossman@cendio.se> for Cendio AB
  2. *
  3. * This is free software; you can redistribute it and/or modify
  4. * it under the terms of the GNU General Public License as published by
  5. * the Free Software Foundation; either version 2 of the License, or
  6. * (at your option) any later version.
  7. *
  8. * This software is distributed in the hope that it will be useful,
  9. * but WITHOUT ANY WARRANTY; without even the implied warranty of
  10. * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  11. * GNU General Public License for more details.
  12. *
  13. * You should have received a copy of the GNU General Public License
  14. * along with this software; if not, write to the Free Software
  15. * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307,
  16. * USA.
  17. */
  18. #ifdef HAVE_CONFIG_H
  19. #include <config.h>
  20. #endif
  21. #include <assert.h>
  22. #include <stdlib.h>
  23. #include <list>
  24. #include <rdr/types.h>
  25. #include <rfb/encodings.h>
  26. #if defined(HAVE_GNUTLS) || defined(HAVE_NETTLE)
  27. #include <rfb/Security.h>
  28. #include <rfb/SecurityClient.h>
  29. #ifdef HAVE_GNUTLS
  30. #include <rfb/CSecurityTLS.h>
  31. #endif
  32. #endif
  33. #include "OptionsDialog.h"
  34. #include "i18n.h"
  35. #include "menukey.h"
  36. #include "parameters.h"
  37. #include "fltk/layout.h"
  38. #include "fltk/util.h"
  39. #include "fltk/Fl_Monitor_Arrangement.h"
  40. #include "fltk/Fl_Navigation.h"
  41. #include <FL/Fl.H>
  42. #include <FL/Fl_Tabs.H>
  43. #include <FL/Fl_Button.H>
  44. #include <FL/Fl_Check_Button.H>
  45. #include <FL/Fl_Return_Button.H>
  46. #include <FL/Fl_Round_Button.H>
  47. #include <FL/Fl_Int_Input.H>
  48. #include <FL/Fl_Choice.H>
  49. using namespace std;
  50. using namespace rdr;
  51. using namespace rfb;
  52. std::map<OptionsCallback*, void*> OptionsDialog::callbacks;
  53. static std::set<OptionsDialog *> instances;
  54. OptionsDialog::OptionsDialog()
  55. : Fl_Window(580, 420, _("TigerVNC Options"))
  56. {
  57. int x, y;
  58. Fl_Navigation *navigation;
  59. Fl_Button *button;
  60. // Odd dimensions to get rid of extra borders
  61. // FIXME: We need to retain the top border on Windows as they don't
  62. // have any separator for the caption, which looks odd
  63. #ifdef WIN32
  64. navigation = new Fl_Navigation(-1, 0, w()+2,
  65. h() - INNER_MARGIN - BUTTON_HEIGHT - OUTER_MARGIN);
  66. #else
  67. navigation = new Fl_Navigation(-1, -1, w()+2,
  68. h()+1 - INNER_MARGIN - BUTTON_HEIGHT - OUTER_MARGIN);
  69. #endif
  70. {
  71. int tx, ty, tw, th;
  72. navigation->client_area(tx, ty, tw, th, 150);
  73. createCompressionPage(tx, ty, tw, th);
  74. createSecurityPage(tx, ty, tw, th);
  75. createInputPage(tx, ty, tw, th);
  76. createDisplayPage(tx, ty, tw, th);
  77. createMiscPage(tx, ty, tw, th);
  78. }
  79. navigation->end();
  80. x = w() - BUTTON_WIDTH * 2 - INNER_MARGIN - OUTER_MARGIN;
  81. y = h() - BUTTON_HEIGHT - OUTER_MARGIN;
  82. button = new Fl_Button(x, y, BUTTON_WIDTH, BUTTON_HEIGHT, _("Cancel"));
  83. button->callback(this->handleCancel, this);
  84. x += BUTTON_WIDTH + INNER_MARGIN;
  85. button = new Fl_Return_Button(x, y, BUTTON_WIDTH, BUTTON_HEIGHT, _("OK"));
  86. button->callback(this->handleOK, this);
  87. callback(this->handleCancel, this);
  88. set_modal();
  89. if (instances.size() == 0)
  90. Fl::add_handler(fltk_event_handler);
  91. instances.insert(this);
  92. }
  93. OptionsDialog::~OptionsDialog()
  94. {
  95. instances.erase(this);
  96. if (instances.size() == 0)
  97. Fl::remove_handler(fltk_event_handler);
  98. }
  99. void OptionsDialog::showDialog(void)
  100. {
  101. static OptionsDialog *dialog = NULL;
  102. if (!dialog)
  103. dialog = new OptionsDialog();
  104. if (dialog->shown())
  105. return;
  106. dialog->show();
  107. }
  108. void OptionsDialog::addCallback(OptionsCallback *cb, void *data)
  109. {
  110. callbacks[cb] = data;
  111. }
  112. void OptionsDialog::removeCallback(OptionsCallback *cb)
  113. {
  114. callbacks.erase(cb);
  115. }
  116. void OptionsDialog::show(void)
  117. {
  118. /* show() gets called for raise events as well */
  119. if (!shown())
  120. loadOptions();
  121. Fl_Window::show();
  122. }
  123. void OptionsDialog::loadOptions(void)
  124. {
  125. /* Compression */
  126. autoselectCheckbox->value(autoSelect);
  127. int encNum = encodingNum(preferredEncoding);
  128. switch (encNum) {
  129. case encodingTight:
  130. tightButton->setonly();
  131. break;
  132. case encodingZRLE:
  133. zrleButton->setonly();
  134. break;
  135. case encodingHextile:
  136. hextileButton->setonly();
  137. break;
  138. #ifdef HAVE_H264
  139. case encodingH264:
  140. h264Button->setonly();
  141. break;
  142. #endif
  143. case encodingRaw:
  144. rawButton->setonly();
  145. break;
  146. }
  147. if (fullColour)
  148. fullcolorCheckbox->setonly();
  149. else {
  150. switch (lowColourLevel) {
  151. case 0:
  152. verylowcolorCheckbox->setonly();
  153. break;
  154. case 1:
  155. lowcolorCheckbox->setonly();
  156. break;
  157. case 2:
  158. mediumcolorCheckbox->setonly();
  159. break;
  160. }
  161. }
  162. char digit[2] = "0";
  163. compressionCheckbox->value(customCompressLevel);
  164. jpegCheckbox->value(!noJpeg);
  165. digit[0] = '0' + compressLevel;
  166. compressionInput->value(digit);
  167. digit[0] = '0' + qualityLevel;
  168. jpegInput->value(digit);
  169. handleAutoselect(autoselectCheckbox, this);
  170. handleCompression(compressionCheckbox, this);
  171. handleJpeg(jpegCheckbox, this);
  172. #if defined(HAVE_GNUTLS) || defined(HAVE_NETTLE)
  173. /* Security */
  174. Security security(SecurityClient::secTypes);
  175. list<U8> secTypes;
  176. list<U8>::iterator iter;
  177. list<U32> secTypesExt;
  178. list<U32>::iterator iterExt;
  179. encNoneCheckbox->value(false);
  180. #ifdef HAVE_GNUTLS
  181. encTLSCheckbox->value(false);
  182. encX509Checkbox->value(false);
  183. #endif
  184. #ifdef HAVE_NETTLE
  185. encRSAAESCheckbox->value(false);
  186. #endif
  187. authNoneCheckbox->value(false);
  188. authVncCheckbox->value(false);
  189. authPlainCheckbox->value(false);
  190. secTypes = security.GetEnabledSecTypes();
  191. for (iter = secTypes.begin(); iter != secTypes.end(); ++iter) {
  192. switch (*iter) {
  193. case secTypeNone:
  194. encNoneCheckbox->value(true);
  195. authNoneCheckbox->value(true);
  196. break;
  197. case secTypeVncAuth:
  198. encNoneCheckbox->value(true);
  199. authVncCheckbox->value(true);
  200. break;
  201. }
  202. }
  203. secTypesExt = security.GetEnabledExtSecTypes();
  204. for (iterExt = secTypesExt.begin(); iterExt != secTypesExt.end(); ++iterExt) {
  205. switch (*iterExt) {
  206. case secTypePlain:
  207. encNoneCheckbox->value(true);
  208. authPlainCheckbox->value(true);
  209. break;
  210. #ifdef HAVE_GNUTLS
  211. case secTypeTLSNone:
  212. encTLSCheckbox->value(true);
  213. authNoneCheckbox->value(true);
  214. break;
  215. case secTypeTLSVnc:
  216. encTLSCheckbox->value(true);
  217. authVncCheckbox->value(true);
  218. break;
  219. case secTypeTLSPlain:
  220. encTLSCheckbox->value(true);
  221. authPlainCheckbox->value(true);
  222. break;
  223. case secTypeX509None:
  224. encX509Checkbox->value(true);
  225. authNoneCheckbox->value(true);
  226. break;
  227. case secTypeX509Vnc:
  228. encX509Checkbox->value(true);
  229. authVncCheckbox->value(true);
  230. break;
  231. case secTypeX509Plain:
  232. encX509Checkbox->value(true);
  233. authPlainCheckbox->value(true);
  234. break;
  235. #endif
  236. #ifdef HAVE_NETTLE
  237. case secTypeRA2:
  238. case secTypeRA256:
  239. encRSAAESCheckbox->value(true);
  240. case secTypeRA2ne:
  241. case secTypeRAne256:
  242. authVncCheckbox->value(true);
  243. authPlainCheckbox->value(true);
  244. break;
  245. #endif
  246. }
  247. }
  248. #ifdef HAVE_GNUTLS
  249. caInput->value(CSecurityTLS::X509CA);
  250. crlInput->value(CSecurityTLS::X509CRL);
  251. handleX509(encX509Checkbox, this);
  252. #endif
  253. #endif
  254. /* Input */
  255. const char *menuKeyBuf;
  256. viewOnlyCheckbox->value(viewOnly);
  257. emulateMBCheckbox->value(emulateMiddleButton);
  258. acceptClipboardCheckbox->value(acceptClipboard);
  259. #if !defined(WIN32) && !defined(__APPLE__)
  260. setPrimaryCheckbox->value(setPrimary);
  261. #endif
  262. sendClipboardCheckbox->value(sendClipboard);
  263. #if !defined(WIN32) && !defined(__APPLE__)
  264. sendPrimaryCheckbox->value(sendPrimary);
  265. #endif
  266. systemKeysCheckbox->value(fullscreenSystemKeys);
  267. menuKeyChoice->value(0);
  268. menuKeyBuf = menuKey;
  269. for (int i = 0; i < getMenuKeySymbolCount(); i++)
  270. if (!strcmp(getMenuKeySymbols()[i].name, menuKeyBuf))
  271. menuKeyChoice->value(i + 1);
  272. /* Display */
  273. if (!fullScreen) {
  274. windowedButton->setonly();
  275. } else {
  276. if (!strcasecmp(fullScreenMode, "all")) {
  277. allMonitorsButton->setonly();
  278. } else if (!strcasecmp(fullScreenMode, "selected")) {
  279. selectedMonitorsButton->setonly();
  280. } else {
  281. currentMonitorButton->setonly();
  282. }
  283. }
  284. monitorArrangement->value(fullScreenSelectedMonitors.getParam());
  285. handleFullScreenMode(selectedMonitorsButton, this);
  286. /* Misc. */
  287. sharedCheckbox->value(shared);
  288. reconnectCheckbox->value(reconnectOnError);
  289. dotCursorCheckbox->value(dotWhenNoCursor);
  290. }
  291. void OptionsDialog::storeOptions(void)
  292. {
  293. /* Compression */
  294. autoSelect.setParam(autoselectCheckbox->value());
  295. if (tightButton->value())
  296. preferredEncoding.setParam(encodingName(encodingTight));
  297. else if (zrleButton->value())
  298. preferredEncoding.setParam(encodingName(encodingZRLE));
  299. else if (hextileButton->value())
  300. preferredEncoding.setParam(encodingName(encodingHextile));
  301. #ifdef HAVE_H264
  302. else if (h264Button->value())
  303. preferredEncoding.setParam(encodingName(encodingH264));
  304. #endif
  305. else if (rawButton->value())
  306. preferredEncoding.setParam(encodingName(encodingRaw));
  307. fullColour.setParam(fullcolorCheckbox->value());
  308. if (verylowcolorCheckbox->value())
  309. lowColourLevel.setParam(0);
  310. else if (lowcolorCheckbox->value())
  311. lowColourLevel.setParam(1);
  312. else if (mediumcolorCheckbox->value())
  313. lowColourLevel.setParam(2);
  314. customCompressLevel.setParam(compressionCheckbox->value());
  315. noJpeg.setParam(!jpegCheckbox->value());
  316. compressLevel.setParam(atoi(compressionInput->value()));
  317. qualityLevel.setParam(atoi(jpegInput->value()));
  318. #if defined(HAVE_GNUTLS) || defined(HAVE_NETTLE)
  319. /* Security */
  320. Security security;
  321. /* Process security types which don't use encryption */
  322. if (encNoneCheckbox->value()) {
  323. if (authNoneCheckbox->value())
  324. security.EnableSecType(secTypeNone);
  325. if (authVncCheckbox->value()) {
  326. security.EnableSecType(secTypeVncAuth);
  327. #ifdef HAVE_NETTLE
  328. security.EnableSecType(secTypeRAne256);
  329. #endif
  330. }
  331. if (authPlainCheckbox->value()) {
  332. security.EnableSecType(secTypePlain);
  333. #ifdef HAVE_NETTLE
  334. security.EnableSecType(secTypeRA2ne);
  335. security.EnableSecType(secTypeRAne256);
  336. #endif
  337. }
  338. }
  339. #ifdef HAVE_GNUTLS
  340. /* Process security types which use TLS encryption */
  341. if (encTLSCheckbox->value()) {
  342. if (authNoneCheckbox->value())
  343. security.EnableSecType(secTypeTLSNone);
  344. if (authVncCheckbox->value())
  345. security.EnableSecType(secTypeTLSVnc);
  346. if (authPlainCheckbox->value())
  347. security.EnableSecType(secTypeTLSPlain);
  348. }
  349. /* Process security types which use X509 encryption */
  350. if (encX509Checkbox->value()) {
  351. if (authNoneCheckbox->value())
  352. security.EnableSecType(secTypeX509None);
  353. if (authVncCheckbox->value())
  354. security.EnableSecType(secTypeX509Vnc);
  355. if (authPlainCheckbox->value())
  356. security.EnableSecType(secTypeX509Plain);
  357. }
  358. CSecurityTLS::X509CA.setParam(caInput->value());
  359. CSecurityTLS::X509CRL.setParam(crlInput->value());
  360. #endif
  361. #ifdef HAVE_NETTLE
  362. if (encRSAAESCheckbox->value()) {
  363. security.EnableSecType(secTypeRA2);
  364. security.EnableSecType(secTypeRA256);
  365. }
  366. #endif
  367. SecurityClient::secTypes.setParam(security.ToString());
  368. #endif
  369. /* Input */
  370. viewOnly.setParam(viewOnlyCheckbox->value());
  371. emulateMiddleButton.setParam(emulateMBCheckbox->value());
  372. acceptClipboard.setParam(acceptClipboardCheckbox->value());
  373. #if !defined(WIN32) && !defined(__APPLE__)
  374. setPrimary.setParam(setPrimaryCheckbox->value());
  375. #endif
  376. sendClipboard.setParam(sendClipboardCheckbox->value());
  377. #if !defined(WIN32) && !defined(__APPLE__)
  378. sendPrimary.setParam(sendPrimaryCheckbox->value());
  379. #endif
  380. fullscreenSystemKeys.setParam(systemKeysCheckbox->value());
  381. if (menuKeyChoice->value() == 0)
  382. menuKey.setParam("");
  383. else {
  384. menuKey.setParam(menuKeyChoice->text());
  385. }
  386. /* Display */
  387. if (windowedButton->value()) {
  388. fullScreen.setParam(false);
  389. } else {
  390. fullScreen.setParam(true);
  391. if (allMonitorsButton->value()) {
  392. fullScreenMode.setParam("All");
  393. } else if (selectedMonitorsButton->value()) {
  394. fullScreenMode.setParam("Selected");
  395. } else {
  396. fullScreenMode.setParam("Current");
  397. }
  398. }
  399. fullScreenSelectedMonitors.setParam(monitorArrangement->value());
  400. /* Misc. */
  401. shared.setParam(sharedCheckbox->value());
  402. reconnectOnError.setParam(reconnectCheckbox->value());
  403. dotWhenNoCursor.setParam(dotCursorCheckbox->value());
  404. std::map<OptionsCallback*, void*>::const_iterator iter;
  405. for (iter = callbacks.begin();iter != callbacks.end();++iter)
  406. iter->first(iter->second);
  407. }
  408. void OptionsDialog::createCompressionPage(int tx, int ty, int tw, int th)
  409. {
  410. Fl_Group *group = new Fl_Group(tx, ty, tw, th, _("Compression"));
  411. int orig_tx, orig_ty;
  412. int col1_ty, col2_ty;
  413. int half_width, full_width;
  414. int height;
  415. tx += OUTER_MARGIN;
  416. ty += OUTER_MARGIN;
  417. full_width = tw - OUTER_MARGIN * 2;
  418. half_width = (full_width - INNER_MARGIN) / 2;
  419. /* AutoSelect checkbox */
  420. autoselectCheckbox = new Fl_Check_Button(LBLRIGHT(tx, ty,
  421. CHECK_MIN_WIDTH,
  422. CHECK_HEIGHT,
  423. _("Auto select")));
  424. autoselectCheckbox->callback(handleAutoselect, this);
  425. ty += CHECK_HEIGHT + INNER_MARGIN;
  426. /* Two columns */
  427. orig_tx = tx;
  428. orig_ty = ty;
  429. /* VNC encoding box */
  430. ty += GROUP_LABEL_OFFSET;
  431. height = GROUP_MARGIN * 2 + TIGHT_MARGIN * 3 + RADIO_HEIGHT * 4;
  432. #ifdef HAVE_H264
  433. height += TIGHT_MARGIN + RADIO_HEIGHT;
  434. #endif
  435. encodingGroup = new Fl_Group(tx, ty, half_width, height,
  436. _("Preferred encoding"));
  437. encodingGroup->box(FL_ENGRAVED_BOX);
  438. encodingGroup->align(FL_ALIGN_LEFT | FL_ALIGN_TOP);
  439. {
  440. tx += GROUP_MARGIN;
  441. ty += GROUP_MARGIN;
  442. tightButton = new Fl_Round_Button(LBLRIGHT(tx, ty,
  443. RADIO_MIN_WIDTH,
  444. RADIO_HEIGHT,
  445. "Tight"));
  446. tightButton->type(FL_RADIO_BUTTON);
  447. ty += RADIO_HEIGHT + TIGHT_MARGIN;
  448. zrleButton = new Fl_Round_Button(LBLRIGHT(tx, ty,
  449. RADIO_MIN_WIDTH,
  450. RADIO_HEIGHT,
  451. "ZRLE"));
  452. zrleButton->type(FL_RADIO_BUTTON);
  453. ty += RADIO_HEIGHT + TIGHT_MARGIN;
  454. hextileButton = new Fl_Round_Button(LBLRIGHT(tx, ty,
  455. RADIO_MIN_WIDTH,
  456. RADIO_HEIGHT,
  457. "Hextile"));
  458. hextileButton->type(FL_RADIO_BUTTON);
  459. ty += RADIO_HEIGHT + TIGHT_MARGIN;
  460. #ifdef HAVE_H264
  461. h264Button = new Fl_Round_Button(LBLRIGHT(tx, ty,
  462. RADIO_MIN_WIDTH,
  463. RADIO_HEIGHT,
  464. "H.264"));
  465. h264Button->type(FL_RADIO_BUTTON);
  466. ty += RADIO_HEIGHT + TIGHT_MARGIN;
  467. #endif
  468. rawButton = new Fl_Round_Button(LBLRIGHT(tx, ty,
  469. RADIO_MIN_WIDTH,
  470. RADIO_HEIGHT,
  471. "Raw"));
  472. rawButton->type(FL_RADIO_BUTTON);
  473. ty += RADIO_HEIGHT + TIGHT_MARGIN;
  474. }
  475. ty += GROUP_MARGIN - TIGHT_MARGIN;
  476. encodingGroup->end();
  477. col1_ty = ty;
  478. /* Second column */
  479. tx = orig_tx + half_width + INNER_MARGIN;
  480. ty = orig_ty;
  481. /* Color box */
  482. ty += GROUP_LABEL_OFFSET;
  483. height = GROUP_MARGIN * 2 + TIGHT_MARGIN * 3 + RADIO_HEIGHT * 4;
  484. colorlevelGroup = new Fl_Group(tx, ty, half_width, height, _("Color level"));
  485. colorlevelGroup->box(FL_ENGRAVED_BOX);
  486. colorlevelGroup->align(FL_ALIGN_LEFT | FL_ALIGN_TOP);
  487. {
  488. tx += GROUP_MARGIN;
  489. ty += GROUP_MARGIN;
  490. fullcolorCheckbox = new Fl_Round_Button(LBLRIGHT(tx, ty,
  491. RADIO_MIN_WIDTH,
  492. RADIO_HEIGHT,
  493. _("Full")));
  494. fullcolorCheckbox->type(FL_RADIO_BUTTON);
  495. ty += RADIO_HEIGHT + TIGHT_MARGIN;
  496. mediumcolorCheckbox = new Fl_Round_Button(LBLRIGHT(tx, ty,
  497. RADIO_MIN_WIDTH,
  498. RADIO_HEIGHT,
  499. _("Medium")));
  500. mediumcolorCheckbox->type(FL_RADIO_BUTTON);
  501. ty += RADIO_HEIGHT + TIGHT_MARGIN;
  502. lowcolorCheckbox = new Fl_Round_Button(LBLRIGHT(tx, ty,
  503. RADIO_MIN_WIDTH,
  504. RADIO_HEIGHT,
  505. _("Low")));
  506. lowcolorCheckbox->type(FL_RADIO_BUTTON);
  507. ty += RADIO_HEIGHT + TIGHT_MARGIN;
  508. verylowcolorCheckbox = new Fl_Round_Button(LBLRIGHT(tx, ty,
  509. RADIO_MIN_WIDTH,
  510. RADIO_HEIGHT,
  511. _("Very low")));
  512. verylowcolorCheckbox->type(FL_RADIO_BUTTON);
  513. ty += RADIO_HEIGHT + TIGHT_MARGIN;
  514. }
  515. ty += GROUP_MARGIN - TIGHT_MARGIN;
  516. colorlevelGroup->end();
  517. col2_ty = ty;
  518. /* Back to normal */
  519. tx = orig_tx;
  520. ty = (col1_ty > col2_ty ? col1_ty : col2_ty) + INNER_MARGIN;
  521. /* Checkboxes */
  522. compressionCheckbox = new Fl_Check_Button(LBLRIGHT(tx, ty,
  523. CHECK_MIN_WIDTH,
  524. CHECK_HEIGHT,
  525. _("Custom compression level:")));
  526. compressionCheckbox->callback(handleCompression, this);
  527. ty += CHECK_HEIGHT + TIGHT_MARGIN;
  528. compressionInput = new Fl_Int_Input(tx + INDENT, ty,
  529. INPUT_HEIGHT, INPUT_HEIGHT,
  530. _("level (0=fast, 9=best)"));
  531. compressionInput->align(FL_ALIGN_RIGHT);
  532. ty += INPUT_HEIGHT + INNER_MARGIN;
  533. jpegCheckbox = new Fl_Check_Button(LBLRIGHT(tx, ty,
  534. CHECK_MIN_WIDTH,
  535. CHECK_HEIGHT,
  536. _("Allow JPEG compression:")));
  537. jpegCheckbox->callback(handleJpeg, this);
  538. ty += CHECK_HEIGHT + TIGHT_MARGIN;
  539. jpegInput = new Fl_Int_Input(tx + INDENT, ty,
  540. INPUT_HEIGHT, INPUT_HEIGHT,
  541. _("quality (0=poor, 9=best)"));
  542. jpegInput->align(FL_ALIGN_RIGHT);
  543. ty += INPUT_HEIGHT + INNER_MARGIN;
  544. group->end();
  545. }
  546. void OptionsDialog::createSecurityPage(int tx, int ty, int tw, int th)
  547. {
  548. #if defined(HAVE_GNUTLS) || defined(HAVE_NETTLE)
  549. Fl_Group *group = new Fl_Group(tx, ty, tw, th, _("Security"));
  550. int orig_tx;
  551. int width, height;
  552. tx += OUTER_MARGIN;
  553. ty += OUTER_MARGIN;
  554. width = tw - OUTER_MARGIN * 2;
  555. orig_tx = tx;
  556. /* Encryption */
  557. ty += GROUP_LABEL_OFFSET;
  558. #if defined(HAVE_GNUTLS) && defined(HAVE_NETTLE)
  559. height = GROUP_MARGIN * 2 + TIGHT_MARGIN * 5 + CHECK_HEIGHT * 4 + (INPUT_LABEL_OFFSET + INPUT_HEIGHT) * 2;
  560. #elif defined(HAVE_GNUTLS)
  561. height = GROUP_MARGIN * 2 + TIGHT_MARGIN * 4 + CHECK_HEIGHT * 3 + (INPUT_LABEL_OFFSET + INPUT_HEIGHT) * 2;
  562. #elif defined(HAVE_NETTLE)
  563. height = GROUP_MARGIN * 2 + TIGHT_MARGIN * 1 + CHECK_HEIGHT * 2;
  564. #endif
  565. encryptionGroup = new Fl_Group(tx, ty, width, height, _("Encryption"));
  566. encryptionGroup->box(FL_ENGRAVED_BOX);
  567. encryptionGroup->align(FL_ALIGN_LEFT | FL_ALIGN_TOP);
  568. {
  569. tx += GROUP_MARGIN;
  570. ty += GROUP_MARGIN;
  571. encNoneCheckbox = new Fl_Check_Button(LBLRIGHT(tx, ty,
  572. CHECK_MIN_WIDTH,
  573. CHECK_HEIGHT,
  574. _("None")));
  575. ty += CHECK_HEIGHT + TIGHT_MARGIN;
  576. #ifdef HAVE_GNUTLS
  577. encTLSCheckbox = new Fl_Check_Button(LBLRIGHT(tx, ty,
  578. CHECK_MIN_WIDTH,
  579. CHECK_HEIGHT,
  580. _("TLS with anonymous certificates")));
  581. ty += CHECK_HEIGHT + TIGHT_MARGIN;
  582. encX509Checkbox = new Fl_Check_Button(LBLRIGHT(tx, ty,
  583. CHECK_MIN_WIDTH,
  584. CHECK_HEIGHT,
  585. _("TLS with X509 certificates")));
  586. encX509Checkbox->callback(handleX509, this);
  587. ty += CHECK_HEIGHT + TIGHT_MARGIN;
  588. ty += INPUT_LABEL_OFFSET;
  589. caInput = new Fl_Input(tx + INDENT, ty,
  590. width - GROUP_MARGIN*2 - INDENT, INPUT_HEIGHT,
  591. _("Path to X509 CA certificate"));
  592. caInput->align(FL_ALIGN_LEFT | FL_ALIGN_TOP);
  593. ty += INPUT_HEIGHT + TIGHT_MARGIN;
  594. ty += INPUT_LABEL_OFFSET;
  595. crlInput = new Fl_Input(tx + INDENT, ty,
  596. width - GROUP_MARGIN*2 - INDENT, INPUT_HEIGHT,
  597. _("Path to X509 CRL file"));
  598. crlInput->align(FL_ALIGN_LEFT | FL_ALIGN_TOP);
  599. ty += INPUT_HEIGHT + TIGHT_MARGIN;
  600. #endif
  601. #ifdef HAVE_NETTLE
  602. encRSAAESCheckbox = new Fl_Check_Button(LBLRIGHT(tx, ty,
  603. CHECK_MIN_WIDTH,
  604. CHECK_HEIGHT,
  605. _("RSA-AES")));
  606. encRSAAESCheckbox->callback(handleRSAAES, this);
  607. ty += CHECK_HEIGHT + TIGHT_MARGIN;
  608. #endif
  609. }
  610. ty += GROUP_MARGIN - TIGHT_MARGIN;
  611. encryptionGroup->end();
  612. /* Back to normal */
  613. tx = orig_tx;
  614. ty += INNER_MARGIN;
  615. /* Authentication */
  616. ty += GROUP_LABEL_OFFSET;
  617. height = GROUP_MARGIN * 2 + TIGHT_MARGIN * 2 + CHECK_HEIGHT * 3;
  618. authenticationGroup = new Fl_Group(tx, ty, width, height, _("Authentication"));
  619. authenticationGroup->box(FL_ENGRAVED_BOX);
  620. authenticationGroup->align(FL_ALIGN_LEFT | FL_ALIGN_TOP);
  621. {
  622. tx += GROUP_MARGIN;
  623. ty += GROUP_MARGIN;
  624. authNoneCheckbox = new Fl_Check_Button(LBLRIGHT(tx, ty,
  625. CHECK_MIN_WIDTH,
  626. CHECK_HEIGHT,
  627. _("None")));
  628. ty += CHECK_HEIGHT + TIGHT_MARGIN;
  629. authVncCheckbox = new Fl_Check_Button(LBLRIGHT(tx, ty,
  630. CHECK_MIN_WIDTH,
  631. CHECK_HEIGHT,
  632. _("Standard VNC (insecure without encryption)")));
  633. ty += CHECK_HEIGHT + TIGHT_MARGIN;
  634. authPlainCheckbox = new Fl_Check_Button(LBLRIGHT(tx, ty,
  635. CHECK_MIN_WIDTH,
  636. CHECK_HEIGHT,
  637. _("Username and password (insecure without encryption)")));
  638. ty += CHECK_HEIGHT + TIGHT_MARGIN;
  639. }
  640. ty += GROUP_MARGIN - TIGHT_MARGIN;
  641. authenticationGroup->end();
  642. /* Back to normal */
  643. tx = orig_tx;
  644. ty += INNER_MARGIN;
  645. group->end();
  646. #endif
  647. }
  648. void OptionsDialog::createInputPage(int tx, int ty, int tw, int th)
  649. {
  650. Fl_Group *group = new Fl_Group(tx, ty, tw, th, _("Input"));
  651. int orig_tx;
  652. int width;
  653. tx += OUTER_MARGIN;
  654. ty += OUTER_MARGIN;
  655. width = tw - OUTER_MARGIN * 2;
  656. viewOnlyCheckbox = new Fl_Check_Button(LBLRIGHT(tx, ty,
  657. CHECK_MIN_WIDTH,
  658. CHECK_HEIGHT,
  659. _("View only (ignore mouse and keyboard)")));
  660. ty += CHECK_HEIGHT + TIGHT_MARGIN;
  661. orig_tx = tx;
  662. /* Mouse */
  663. ty += GROUP_LABEL_OFFSET;
  664. mouseGroup = new Fl_Group(tx, ty, width, 0, _("Mouse"));
  665. mouseGroup->box(FL_ENGRAVED_BOX);
  666. mouseGroup->align(FL_ALIGN_LEFT | FL_ALIGN_TOP);
  667. /* Needed for final resize to work sanely */
  668. mouseGroup->resizable(NULL);
  669. {
  670. tx += GROUP_MARGIN;
  671. ty += GROUP_MARGIN;
  672. emulateMBCheckbox = new Fl_Check_Button(LBLRIGHT(tx, ty,
  673. CHECK_MIN_WIDTH,
  674. CHECK_HEIGHT,
  675. _("Emulate middle mouse button")));
  676. ty += CHECK_HEIGHT + TIGHT_MARGIN;
  677. dotCursorCheckbox = new Fl_Check_Button(LBLRIGHT(tx, ty,
  678. CHECK_MIN_WIDTH,
  679. CHECK_HEIGHT,
  680. _("Show dot when no cursor")));
  681. ty += CHECK_HEIGHT + TIGHT_MARGIN;
  682. }
  683. ty += GROUP_MARGIN - TIGHT_MARGIN;
  684. mouseGroup->end();
  685. mouseGroup->size(mouseGroup->w(), ty - mouseGroup->y());
  686. /* Back to normal */
  687. tx = orig_tx;
  688. ty += INNER_MARGIN;
  689. /* Keyboard */
  690. ty += GROUP_LABEL_OFFSET;
  691. keyboardGroup = new Fl_Group(tx, ty, width, 0, _("Keyboard"));
  692. keyboardGroup->box(FL_ENGRAVED_BOX);
  693. keyboardGroup->align(FL_ALIGN_LEFT | FL_ALIGN_TOP);
  694. /* Needed for final resize to work sanely */
  695. keyboardGroup->resizable(NULL);
  696. {
  697. tx += GROUP_MARGIN;
  698. ty += GROUP_MARGIN;
  699. systemKeysCheckbox = new Fl_Check_Button(LBLRIGHT(tx, ty,
  700. CHECK_MIN_WIDTH,
  701. CHECK_HEIGHT,
  702. _("Pass system keys directly to server (full screen)")));
  703. ty += CHECK_HEIGHT + TIGHT_MARGIN;
  704. menuKeyChoice = new Fl_Choice(LBLLEFT(tx, ty, 150, CHOICE_HEIGHT, _("Menu key")));
  705. fltk_menu_add(menuKeyChoice, _("None"), 0, NULL, (void*)0, FL_MENU_DIVIDER);
  706. for (int i = 0; i < getMenuKeySymbolCount(); i++)
  707. fltk_menu_add(menuKeyChoice, getMenuKeySymbols()[i].name, 0, NULL, 0, 0);
  708. ty += CHOICE_HEIGHT + TIGHT_MARGIN;
  709. }
  710. ty += GROUP_MARGIN - TIGHT_MARGIN;
  711. keyboardGroup->end();
  712. keyboardGroup->size(keyboardGroup->w(), ty - keyboardGroup->y());
  713. /* Back to normal */
  714. tx = orig_tx;
  715. ty += INNER_MARGIN;
  716. /* Clipboard */
  717. ty += GROUP_LABEL_OFFSET;
  718. clipboardGroup = new Fl_Group(tx, ty, width, 0, _("Clipboard"));
  719. clipboardGroup->box(FL_ENGRAVED_BOX);
  720. clipboardGroup->align(FL_ALIGN_LEFT | FL_ALIGN_TOP);
  721. /* Needed for final resize to work sanely */
  722. clipboardGroup->resizable(NULL);
  723. {
  724. tx += GROUP_MARGIN;
  725. ty += GROUP_MARGIN;
  726. acceptClipboardCheckbox = new Fl_Check_Button(LBLRIGHT(tx, ty,
  727. CHECK_MIN_WIDTH,
  728. CHECK_HEIGHT,
  729. _("Accept clipboard from server")));
  730. acceptClipboardCheckbox->callback(handleClipboard, this);
  731. ty += CHECK_HEIGHT + TIGHT_MARGIN;
  732. #if !defined(WIN32) && !defined(__APPLE__)
  733. setPrimaryCheckbox = new Fl_Check_Button(LBLRIGHT(tx + INDENT, ty,
  734. CHECK_MIN_WIDTH,
  735. CHECK_HEIGHT,
  736. _("Also set primary selection")));
  737. ty += CHECK_HEIGHT + TIGHT_MARGIN;
  738. #endif
  739. sendClipboardCheckbox = new Fl_Check_Button(LBLRIGHT(tx, ty,
  740. CHECK_MIN_WIDTH,
  741. CHECK_HEIGHT,
  742. _("Send clipboard to server")));
  743. sendClipboardCheckbox->callback(handleClipboard, this);
  744. ty += CHECK_HEIGHT + TIGHT_MARGIN;
  745. #if !defined(WIN32) && !defined(__APPLE__)
  746. sendPrimaryCheckbox = new Fl_Check_Button(LBLRIGHT(tx + INDENT, ty,
  747. CHECK_MIN_WIDTH,
  748. CHECK_HEIGHT,
  749. _("Send primary selection as clipboard")));
  750. ty += CHECK_HEIGHT + TIGHT_MARGIN;
  751. #endif
  752. }
  753. ty += GROUP_MARGIN - TIGHT_MARGIN;
  754. clipboardGroup->end();
  755. clipboardGroup->size(clipboardGroup->w(), ty - clipboardGroup->y());
  756. /* Back to normal */
  757. tx = orig_tx;
  758. ty += INNER_MARGIN;
  759. group->end();
  760. }
  761. void OptionsDialog::createDisplayPage(int tx, int ty, int tw, int th)
  762. {
  763. Fl_Group *group = new Fl_Group(tx, ty, tw, th, _("Display"));
  764. int orig_tx;
  765. int width;
  766. tx += OUTER_MARGIN;
  767. ty += OUTER_MARGIN;
  768. width = tw - OUTER_MARGIN * 2;
  769. orig_tx = tx;
  770. /* Display mode */
  771. ty += GROUP_LABEL_OFFSET;
  772. displayModeGroup = new Fl_Group(tx, ty, width, 0, _("Display mode"));
  773. displayModeGroup->box(FL_ENGRAVED_BOX);
  774. displayModeGroup->align(FL_ALIGN_LEFT | FL_ALIGN_TOP);
  775. /* Needed for final resize to work sanely */
  776. displayModeGroup->resizable(NULL);
  777. {
  778. tx += GROUP_MARGIN;
  779. ty += GROUP_MARGIN;
  780. width -= GROUP_MARGIN * 2;
  781. windowedButton = new Fl_Round_Button(LBLRIGHT(tx, ty,
  782. RADIO_MIN_WIDTH,
  783. RADIO_HEIGHT,
  784. _("Windowed")));
  785. windowedButton->type(FL_RADIO_BUTTON);
  786. windowedButton->callback(handleFullScreenMode, this);
  787. ty += RADIO_HEIGHT + TIGHT_MARGIN;
  788. currentMonitorButton = new Fl_Round_Button(LBLRIGHT(tx, ty,
  789. RADIO_MIN_WIDTH,
  790. RADIO_HEIGHT,
  791. _("Full screen on current monitor")));
  792. currentMonitorButton->type(FL_RADIO_BUTTON);
  793. currentMonitorButton->callback(handleFullScreenMode, this);
  794. ty += RADIO_HEIGHT + TIGHT_MARGIN;
  795. allMonitorsButton = new Fl_Round_Button(LBLRIGHT(tx, ty,
  796. RADIO_MIN_WIDTH,
  797. RADIO_HEIGHT,
  798. _("Full screen on all monitors")));
  799. allMonitorsButton->type(FL_RADIO_BUTTON);
  800. allMonitorsButton->callback(handleFullScreenMode, this);
  801. ty += RADIO_HEIGHT + TIGHT_MARGIN;
  802. selectedMonitorsButton = new Fl_Round_Button(LBLRIGHT(tx, ty,
  803. RADIO_MIN_WIDTH,
  804. RADIO_HEIGHT,
  805. _("Full screen on selected monitor(s)")));
  806. selectedMonitorsButton->type(FL_RADIO_BUTTON);
  807. selectedMonitorsButton->callback(handleFullScreenMode, this);
  808. ty += RADIO_HEIGHT + TIGHT_MARGIN;
  809. monitorArrangement = new Fl_Monitor_Arrangement(
  810. tx + INDENT, ty,
  811. width - INDENT, 150);
  812. ty += 150 + TIGHT_MARGIN;
  813. }
  814. ty += GROUP_MARGIN - TIGHT_MARGIN;
  815. displayModeGroup->end();
  816. displayModeGroup->size(displayModeGroup->w(),
  817. ty - displayModeGroup->y());
  818. /* Back to normal */
  819. tx = orig_tx;
  820. ty += INNER_MARGIN;
  821. width = tw - OUTER_MARGIN * 2;
  822. group->end();
  823. }
  824. void OptionsDialog::createMiscPage(int tx, int ty, int tw, int th)
  825. {
  826. Fl_Group *group = new Fl_Group(tx, ty, tw, th, _("Miscellaneous"));
  827. tx += OUTER_MARGIN;
  828. ty += OUTER_MARGIN;
  829. sharedCheckbox = new Fl_Check_Button(LBLRIGHT(tx, ty,
  830. CHECK_MIN_WIDTH,
  831. CHECK_HEIGHT,
  832. _("Shared (don't disconnect other viewers)")));
  833. ty += CHECK_HEIGHT + TIGHT_MARGIN;
  834. reconnectCheckbox = new Fl_Check_Button(LBLRIGHT(tx, ty,
  835. CHECK_MIN_WIDTH,
  836. CHECK_HEIGHT,
  837. _("Ask to reconnect on connection errors")));
  838. ty += CHECK_HEIGHT + TIGHT_MARGIN;
  839. group->end();
  840. }
  841. void OptionsDialog::handleAutoselect(Fl_Widget *widget, void *data)
  842. {
  843. OptionsDialog *dialog = (OptionsDialog*)data;
  844. if (dialog->autoselectCheckbox->value()) {
  845. dialog->encodingGroup->deactivate();
  846. dialog->colorlevelGroup->deactivate();
  847. } else {
  848. dialog->encodingGroup->activate();
  849. dialog->colorlevelGroup->activate();
  850. }
  851. // JPEG setting is also affected by autoselection
  852. dialog->handleJpeg(dialog->jpegCheckbox, dialog);
  853. }
  854. void OptionsDialog::handleCompression(Fl_Widget *widget, void *data)
  855. {
  856. OptionsDialog *dialog = (OptionsDialog*)data;
  857. if (dialog->compressionCheckbox->value())
  858. dialog->compressionInput->activate();
  859. else
  860. dialog->compressionInput->deactivate();
  861. }
  862. void OptionsDialog::handleJpeg(Fl_Widget *widget, void *data)
  863. {
  864. OptionsDialog *dialog = (OptionsDialog*)data;
  865. if (dialog->jpegCheckbox->value() &&
  866. !dialog->autoselectCheckbox->value())
  867. dialog->jpegInput->activate();
  868. else
  869. dialog->jpegInput->deactivate();
  870. }
  871. void OptionsDialog::handleX509(Fl_Widget *widget, void *data)
  872. {
  873. OptionsDialog *dialog = (OptionsDialog*)data;
  874. if (dialog->encX509Checkbox->value()) {
  875. dialog->caInput->activate();
  876. dialog->crlInput->activate();
  877. } else {
  878. dialog->caInput->deactivate();
  879. dialog->crlInput->deactivate();
  880. }
  881. }
  882. void OptionsDialog::handleRSAAES(Fl_Widget *widget, void *data)
  883. {
  884. OptionsDialog *dialog = (OptionsDialog*)data;
  885. if (dialog->encRSAAESCheckbox->value()) {
  886. dialog->authVncCheckbox->value(true);
  887. dialog->authPlainCheckbox->value(true);
  888. }
  889. }
  890. void OptionsDialog::handleClipboard(Fl_Widget *widget, void *data)
  891. {
  892. #if !defined(WIN32) && !defined(__APPLE__)
  893. OptionsDialog *dialog = (OptionsDialog*)data;
  894. if (dialog->acceptClipboardCheckbox->value())
  895. dialog->setPrimaryCheckbox->activate();
  896. else
  897. dialog->setPrimaryCheckbox->deactivate();
  898. if (dialog->sendClipboardCheckbox->value())
  899. dialog->sendPrimaryCheckbox->activate();
  900. else
  901. dialog->sendPrimaryCheckbox->deactivate();
  902. #endif
  903. }
  904. void OptionsDialog::handleFullScreenMode(Fl_Widget *widget, void *data)
  905. {
  906. OptionsDialog *dialog = (OptionsDialog*)data;
  907. if (dialog->selectedMonitorsButton->value()) {
  908. dialog->monitorArrangement->activate();
  909. } else {
  910. dialog->monitorArrangement->deactivate();
  911. }
  912. }
  913. void OptionsDialog::handleCancel(Fl_Widget *widget, void *data)
  914. {
  915. OptionsDialog *dialog = (OptionsDialog*)data;
  916. dialog->hide();
  917. }
  918. void OptionsDialog::handleOK(Fl_Widget *widget, void *data)
  919. {
  920. OptionsDialog *dialog = (OptionsDialog*)data;
  921. dialog->hide();
  922. dialog->storeOptions();
  923. }
  924. int OptionsDialog::fltk_event_handler(int event)
  925. {
  926. std::set<OptionsDialog *>::iterator iter;
  927. if (event != FL_SCREEN_CONFIGURATION_CHANGED)
  928. return 0;
  929. // Refresh monitor arrangement widget to match the parameter settings after
  930. // screen configuration has changed. The MonitorArrangement index doesn't work
  931. // the same way as the FLTK screen index.
  932. for (iter = instances.begin(); iter != instances.end(); iter++)
  933. Fl::add_timeout(0, handleScreenConfigTimeout, (*iter));
  934. return 0;
  935. }
  936. void OptionsDialog::handleScreenConfigTimeout(void *data)
  937. {
  938. OptionsDialog *self = (OptionsDialog *)data;
  939. assert(self);
  940. self->monitorArrangement->value(fullScreenSelectedMonitors.getParam());
  941. }