• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

gansm / finalcut / #760

06 Feb 2026 01:45AM UTC coverage: 69.064% (-0.02%) from 69.083%
#760

push

travis-ci

gansm
Reduce pointer arithmetic and use safe iterators more often

206 of 432 new or added lines in 27 files covered. (47.69%)

25 existing lines in 6 files now uncovered.

37622 of 54474 relevant lines covered (69.06%)

243.8 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

22.49
/final/fapplication.cpp
1
/***********************************************************************
2
* fapplication.cpp - Manages the application events                    *
3
*                                                                      *
4
* This file is part of the FINAL CUT widget toolkit                    *
5
*                                                                      *
6
* Copyright 2013-2026 Markus Gans                                      *
7
*                                                                      *
8
* FINAL CUT is free software; you can redistribute it and/or modify    *
9
* it under the terms of the GNU Lesser General Public License as       *
10
* published by the Free Software Foundation; either version 3 of       *
11
* the License, or (at your option) any later version.                  *
12
*                                                                      *
13
* FINAL CUT is distributed in the hope that it will be useful, but     *
14
* WITHOUT ANY WARRANTY; without even the implied warranty of           *
15
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the        *
16
* GNU Lesser General Public License for more details.                  *
17
*                                                                      *
18
* You should have received a copy of the GNU Lesser General Public     *
19
* License along with this program.  If not, see                        *
20
* <http://www.gnu.org/licenses/>.                                      *
21
***********************************************************************/
22

23
#include <chrono>
24
#include <fstream>
25
#include <iostream>
26
#include <memory>
27
#include <ostream>
28
#include <string>
29
#include <thread>
30

31
#include "final/dialog/fmessagebox.h"
32
#include "final/fapplication.h"
33
#include "final/fevent.h"
34
#include "final/fstartoptions.h"
35
#include "final/fwidgetcolors.h"
36
#include "final/input/fmouse.h"
37
#include "final/menu/fmenubar.h"
38
#include "final/menu/fmenu.h"
39
#include "final/output/tty/ftermdata.h"
40
#include "final/output/tty/ftermios.h"
41
#include "final/output/tty/ftermxterminal.h"
42
#include "final/util/flogger.h"
43
#include "final/util/flog.h"
44
#include "final/widget/fstatusbar.h"
45
#include "final/widget/fwindow.h"
46

47
namespace finalcut
48
{
49

50
namespace internal
51
{
52

53
struct var
54
{
55
  static FApplication* app_object;  // Global application object
56
  static bool          exit_loop;   // Flag to exit the local event loop
57
};
58

59
FApplication*  var::app_object {nullptr};
60
bool           var::exit_loop  {false};
61

62
}  // namespace internal
63

64
// Static attributes
65
FWidget*        FWidget::main_widget             {nullptr};  // main application widget
66
FWidget*        FWidget::active_window           {nullptr};  // the active window
67
FWidget*        FWidget::focus_widget            {nullptr};  // has keyboard input focus
68
FWidget*        FWidget::clicked_widget          {nullptr};  // is focused by click
69
FWidget*        FApplication::clicked_widget     {nullptr};  // is focused by click
70
FWidget*        FApplication::wheel_widget       {nullptr};  // is focused on wheel
71
FWidget*        FWidget::open_menu               {nullptr};  // currently open menu
72
FWidget*        FWidget::move_resize_widget      {nullptr};  // move or resize by keyboard
73
FWidget*        FApplication::keyboard_widget    {nullptr};  // has the keyboard focus
74
int             FApplication::loop_level         {0};        // event loop level
75
int             FApplication::quit_code          {EXIT_SUCCESS};
76
bool            FApplication::quit_now           {false};
77
uInt64          FApplication::next_event_wait    {5000};     // 5 ms (200 Hz)
78
TimeValue       FApplication::time_last_event    {};
79
std::streambuf* FApplication::default_clog_rdbuf {nullptr};
80

81
// FEvent friend function forward declaration
82
void setSend (FEvent&, bool = true);
83
void setQueued (FEvent&, bool = true);
84

85

86
//----------------------------------------------------------------------
87
// class FApplication
88
//----------------------------------------------------------------------
89

90
// constructors and destructor
91
//----------------------------------------------------------------------
92
FApplication::FApplication (const int& arg_c, char* arg_v[])
2✔
93
  : FWidget{processParameters(Args(arg_v, arg_v + arg_c))}
4✔
94
  , app_args{arg_v, arg_v + arg_c}
10✔
95
{
96
  if ( quit_now )
2✔
97
    return;
×
98

99
  if ( internal::var::app_object )
2✔
100
  {
101
    setExitMessage("FApplication: There should be "
×
102
                   "only one application object");
103
    FApplication::exit(EXIT_FAILURE);
×
104
    return;
×
105
  }
106

107
  // First define the application object
108
  internal::var::app_object = this;
2✔
109

110
  init();
2✔
111
}
×
112

113
//----------------------------------------------------------------------
114
FApplication::~FApplication()  // destructor
2✔
115
{
116
  internal::var::app_object = nullptr;
2✔
117

118
  if ( eventInQueue() )
2✔
119
    event_queue.clear();
×
120

121
  resetLog();
2✔
122
}
2✔
123

124

125
// public methods of FApplication
126
//----------------------------------------------------------------------
127
auto FApplication::getApplicationObject() -> FApplication*
50✔
128
{
129
  return internal::var::app_object;
50✔
130
}
131

132
//----------------------------------------------------------------------
133
auto FApplication::getKeyboardWidget() -> FWidget*
46✔
134
{
135
  return keyboard_widget;
46✔
136
}
137

138
//----------------------------------------------------------------------
139
auto FApplication::getLog() -> FLogPtr&
20✔
140
{
141
  // Global logger object
142
  static const auto& logger = std::make_unique<FLogPtr>();
20✔
143

144
  if ( logger && *logger == nullptr
40✔
145
    && ( ! isQuit() || getStartOptions().is_being_initialized) )
40✔
146
  {
147
    *logger = std::make_shared<FLogger>();
2✔
148

149
    // Set the logger as rdbuf of clog
150
    std::clog.rdbuf(logger->get());
2✔
151
  }
152

153
  return *logger;
20✔
154
}
155

156
//----------------------------------------------------------------------
157
void FApplication::setLog (const FLogPtr& log)
11✔
158
{
159
  FLogPtr& logger = getLog();
11✔
160
  logger.reset();
11✔
161
  logger = log;
11✔
162

163
  // Set the logger as rdbuf of clog
164
  std::clog.rdbuf(logger.get());
11✔
165
}
11✔
166

167
//----------------------------------------------------------------------
168
auto FApplication::isQuit() -> bool
808✔
169
{
170
  return internal::var::app_object ? quit_now : true;
1,542✔
171
}
734✔
172

173
//----------------------------------------------------------------------
174
#if defined(UNIT_TEST)
175
void FApplication::start()  // Simulates application start
3✔
176
{
177
  quit_now = false;
3✔
178
}
3✔
179
#endif  // defined(UNIT_TEST)
180

181
//----------------------------------------------------------------------
182
auto FApplication::exec() -> int  // run
×
183
{
184
  if ( quit_now )
×
185
  {
186
    quit_now = false;
×
187
    return quit_code;
×
188
  }
189

190
  quit_code = EXIT_SUCCESS;
×
191
  enterLoop();
×
192
  return quit_code;
×
193
}
194

195
//----------------------------------------------------------------------
196
auto FApplication::enterLoop() -> int  // event loop
×
197
{
198
  loop_level++;
×
199
  quit_now = false;
×
200

201
  const bool old_app_exit_loop = internal::var::exit_loop;
×
202
  internal::var::exit_loop = false;
×
203

204
  while ( ! (quit_now || internal::var::exit_loop) )
×
205
    processNextEvent();
×
206

207
  internal::var::exit_loop = old_app_exit_loop;
×
208
  loop_level--;
×
209
  return 0;
×
210
}
211

212
//----------------------------------------------------------------------
213
void FApplication::exitLoop() const
×
214
{
215
  internal::var::exit_loop = true;
×
216
}
×
217

218
//----------------------------------------------------------------------
219
void FApplication::exit (int retcode)
4✔
220
{
221
  quit_now  = true;
4✔
222
  quit_code = retcode;
4✔
223
}
4✔
224

225
//----------------------------------------------------------------------
226
void FApplication::quit() const
×
227
{
NEW
228
  FApplication::exit(EXIT_SUCCESS);
×
229
}
×
230

231
//----------------------------------------------------------------------
232
auto FApplication::sendEvent (FObject* receiver, FEvent* event ) -> bool
171✔
233
{
234
  if ( quit_now
171✔
235
    || internal::var::exit_loop
121✔
236
    || ! (bool(receiver) && bool(event))
121✔
237
    || ! isEventProcessable (receiver, event) )
292✔
238
    return false;
50✔
239

240
  // Sends the event event directly to receiver
241
  const auto& ret = receiver->event(event);
121✔
242
  setSend(*event);
121✔
243
  return ret;
121✔
244
}
245

246
//----------------------------------------------------------------------
247
void FApplication::queueEvent (FObject* receiver, FEvent* event)
×
248
{
249
  if ( ! (bool(receiver) && bool(event)) )
×
250
    return;
×
251

252
  // queue this event
253
  setQueued(*event);
×
254
  event_queue.emplace_back (receiver, event);
×
255
}
256

257
//----------------------------------------------------------------------
258
void FApplication::sendQueuedEvents()
×
259
{
260
  while ( eventInQueue() )
×
261
  {
262
    const auto& event_pair = event_queue.front();
×
263
    setQueued(*event_pair.second, false);
×
264
    sendEvent(event_pair.first, event_pair.second);
×
265
    event_queue.pop_front();
×
266
  }
267
}
×
268

269
//----------------------------------------------------------------------
270
auto FApplication::eventInQueue() const -> bool
2✔
271
{
272
  if ( internal::var::app_object )
2✔
273
    return ( ! event_queue.empty() );
×
274

275
  return false;
2✔
276
}
277

278
//----------------------------------------------------------------------
279
auto FApplication::removeQueuedEvent (const FObject* receiver) -> bool
×
280
{
281
  if ( ! eventInQueue() )
×
282
    return false;
×
283

284
  if ( ! receiver )
×
285
    return false;
×
286

287
  bool retval{false};
×
288
  auto iter = event_queue.cbegin();
×
289

290
  while ( iter != event_queue.cend() )
×
291
  {
292
    if ( iter->first == receiver )
×
293
    {
294
      iter = event_queue.erase(iter);
×
295
      retval = true;
×
296
    }
297
    else
298
      ++iter;
×
299
  }
300

301
  return retval;
×
302
}
303

304
//----------------------------------------------------------------------
305
void FApplication::registerMouseHandler (const FMouseHandler& fn)
6✔
306
{
307
  mouse_handler_list.push_back(fn);
6✔
308
}
6✔
309

310
//----------------------------------------------------------------------
311
void FApplication::initTerminal()
×
312
{
313
  if ( ! isQuit() )
×
314
    FWidget::initTerminal();
×
315
}
×
316

317
//----------------------------------------------------------------------
318
void FApplication::setDefaultTheme()
×
319
{
320
  auto foutput = FVTerm::getFOutput();
×
321

322
  if ( foutput->getMaxColor() < 16 )  // for 8 color mode
×
323
  {
324
    if ( getStartOptions().color_change )
×
325
      foutput->setColorPaletteTheme<default8ColorPalette>();
×
326

327
    setColorTheme<default8ColorTheme>();
×
328
  }
329
  else
330
  {
331
    if ( getStartOptions().color_change )
×
332
      foutput->setColorPaletteTheme<default16ColorPalette>();
×
333

334
    setColorTheme<default16ColorTheme>();
×
335
  }
336
}
×
337

338
//----------------------------------------------------------------------
339
void FApplication::setDarkTheme()
×
340
{
341
  auto foutput = FVTerm::getFOutput();
×
342

343
  if ( getStartOptions().color_change )
×
344
    foutput->setColorPaletteTheme<default16DarkColorPalette>();
×
345

346
  if ( foutput->getMaxColor() < 16 )  // for 8 color mode
×
347
    setColorTheme<default8ColorDarkTheme>();
×
348
  else
349
    setColorTheme<default16ColorDarkTheme>();
×
350
}
×
351

352
//----------------------------------------------------------------------
353
void FApplication::setLogFile (const FString& filename)
×
354
{
355
  auto& log_stream = getStartOptions().logfile_stream;
×
356
  log_stream.open(filename.toString(), std::ofstream::out);
×
357

358
  if ( log_stream.is_open() )
×
359
  {
360
    // Get the global logger object
361
    const auto& log = FApplication::getLog();
×
362

363
    if ( ! log )
×
364
      return;
×
365

366
    log->setOutputStream(log_stream);
×
367
    log->enableTimestamp();
×
368
    log->setLineEnding (FLog::LineEnding::LF);
×
369
  }
370
  else
371
  {
372
    setExitMessage ("Could not open log file \"" + filename + "\"");
×
373
    exit(EXIT_FAILURE);
×
374
  }
375
}
376

377
//----------------------------------------------------------------------
378
void FApplication::setKeyboardWidget (FWidget* widget)
×
379
{
380
  keyboard_widget = widget;
×
381
}
×
382

383
//----------------------------------------------------------------------
384
void FApplication::closeConfirmationDialog (FWidget* w, FCloseEvent* ev)
×
385
{
386
  FApplication::unsetMoveResizeMode(FMouseData{});
×
387
  const auto& ret = \
388
      FMessageBox::info ( w, "Quit"
×
389
                        , "Do you really want\n"
390
                          "to quit the program ?"
391
                        , FMessageBox::ButtonType::Yes
392
                        , FMessageBox::ButtonType::No );
×
393

394
  if ( ret == FMessageBox::ButtonType::Yes )
×
395
    ev->accept();
×
396
  else
397
  {
398
    ev->ignore();
×
399

400
    // Status bar restore after closing the FMessageBox
401
    drawStatusBarMessage();
×
402
  }
403
}
×
404

405

406
// protected methods of FApplication
407
//----------------------------------------------------------------------
408
void FApplication::processExternalUserEvent()
×
409
{
410
  // This method can be overloaded and replaced by own code
411
}
×
412

413

414
// private methods of FApplication
415
//----------------------------------------------------------------------
416
void FApplication::init()
2✔
417
{
418
  // FApplication cannot have a second child widget
419
  setMaxChildren(1);
2✔
420

421
  // Initialize the last event time
422
  time_last_event = TimeValue{};
2✔
423

424
  // Initialize keyboard
425
  static auto& keyboard = FKeyboard::getInstance();
2✔
426
  auto cmd1 = [this] () { this->keyPressed(); };
×
427
  auto cmd2 = [this] () { this->keyReleased(); };
×
428
  auto cmd3 = [this] () { this->escapeKeyPressed(); };
×
429
  auto cmd4 = [this] () { this->mouseTracking(); };
×
430
  FKeyboardCommand key_cmd1 (cmd1);
2✔
431
  FKeyboardCommand key_cmd2 (cmd2);
2✔
432
  FKeyboardCommand key_cmd3 (cmd3);
2✔
433
  FKeyboardCommand key_cmd4 (cmd4);
2✔
434
  keyboard.setPressCommand (key_cmd1);
2✔
435
  keyboard.setReleaseCommand (key_cmd2);
2✔
436
  keyboard.setEscPressedCommand (key_cmd3);
2✔
437
  keyboard.setMouseTrackingCommand (key_cmd4);
2✔
438
  // Set the keyboard keypress timeout
439
  keyboard.setKeypressTimeout (key_timeout);
2✔
440

441
  // Initialize mouse control
442
  static auto& mouse = FMouseControl::getInstance();
2✔
443
  static auto& xterm = FTermXTerminal::getInstance();
2✔
444
  auto cmd5 = [this] (const auto& md) { this->mouseEvent(md); };
×
445
  auto cmd6 = [] () { xterm.setMouseSupport(true); };
×
446
  auto cmd7 = [] () { xterm.setMouseSupport(false); };
×
447
  FMouseCommand mouse_event_cmd (cmd5);
2✔
448
  FMouseCommand enable_xterm_mouse_cmd (cmd6);
2✔
449
  FMouseCommand disable_xterm_mouse_cmd (cmd7);
2✔
450
  mouse.setEventCommand (mouse_event_cmd);
2✔
451
  mouse.setEnableXTermMouseCommand (enable_xterm_mouse_cmd);
2✔
452
  mouse.setDisableXTermMouseCommand (disable_xterm_mouse_cmd);
2✔
453
  // Set stdin number for a gpm-mouse
454
  mouse.setStdinNo (FTermios::getStdIn());
2✔
455
  // Set the default double click interval
456
  mouse.setDblclickInterval (dblclick_interval);
2✔
457

458
  // Restister mouse handler callbacks
459
  registerMouseHandler (FApplication::determineClickedWidget);
2✔
460
  registerMouseHandler (FApplication::determineWheelWidget);
2✔
461
  registerMouseHandler (FApplication::unsetMoveResizeMode);
2✔
462

463
  // Initialize logging
464
  if ( ! getStartOptions().logfile_stream.is_open() )
2✔
465
    getLog()->setLineEnding(FLog::LineEnding::CRLF);
2✔
466
}
2✔
467

468
//----------------------------------------------------------------------
469
void FApplication::setTerminalEncoding (const FString& enc_str)
×
470
{
471
  const auto& enc = enc_str.toLower();
×
472

473
  if ( enc.includes("utf8") )
×
474
    getStartOptions().encoding = Encoding::UTF8;
×
475
  else if ( enc.includes("vt100") )
×
476
    getStartOptions().encoding = Encoding::VT100;
×
477
  else if ( enc.includes("pc") )
×
478
    getStartOptions().encoding = Encoding::PC;
×
479
  else if ( enc.includes("ascii") )
×
480
    getStartOptions().encoding = Encoding::ASCII;
×
481
  else if ( enc.includes("help") )
×
482
    showParameterUsage();
×
483
  else
484
  {
485
    setExitMessage ( "Unknown encoding \"" + enc_str
×
486
                   + "\"\n(Valid encodings are utf8, "
×
487
                   + "vt100, pc and ascii)" );
×
488
    exit(EXIT_FAILURE);
×
489
  }
490
}
×
491

492
//----------------------------------------------------------------------
493
inline auto FApplication::getLongOptions() -> const std::vector<CmdOption>&
2✔
494
{
495
  static const std::vector<CmdOption>& long_options =
496
  {
497
    {"encoding",                 required_argument, nullptr,  'e' },
498
    {"log-file",                 required_argument, nullptr,  'l' },
499
    {"no-mouse",                 no_argument,       nullptr,  'm' },
500
    {"no-optimized-cursor",      no_argument,       nullptr,  'o' },
501
    {"no-terminal-detection",    no_argument,       nullptr,  'd' },
502
    {"no-terminal-data-request", no_argument,       nullptr,  'r' },
503
    {"no-terminal-focus-events", no_argument,       nullptr,  'f' },
504
    {"no-color-change",          no_argument,       nullptr,  'c' },
505
    {"no-sgr-optimizer",         no_argument,       nullptr,  's' },
506
    {"vgafont",                  no_argument,       nullptr,  'v' },
507
    {"newfont",                  no_argument,       nullptr,  'n' },
508
    {"dark-theme",               no_argument,       nullptr,  't' },
509

510
  #if defined(__FreeBSD__) || defined(__DragonFly__)
511
    {"no-esc-for-alt-meta",      no_argument,       nullptr,  'E' },
512
    {"no-cursorstyle-change",    no_argument,       nullptr,  'C' },
513
  #elif defined(__NetBSD__) || defined(__OpenBSD__)
514
    {"no-esc-for-alt-meta",      no_argument,       nullptr,  'E' },
515
  #endif
516

517
    {nullptr,                    0,                 nullptr,  0   }
518
  };
6✔
519
  return long_options;
2✔
520
}
521

522
//----------------------------------------------------------------------
523
inline void FApplication::setCmdOptionsMap (CmdMap& cmd_map)
2✔
524
{
525
  auto enc = [] (const auto& s) { FApplication::setTerminalEncoding(s); };
×
526
  auto log = [] (const auto& s) { FApplication::setLogFile(s); };
×
527
  auto opt = &FApplication::getStartOptions;
2✔
528

529
  // --encoding
530
  cmd_map['e'] = [enc] (const auto& arg) { enc(FString(arg)); };
2✔
531
  // --log-file
532
  cmd_map['l'] = [log] (const auto& arg) { log(FString(arg)); };
2✔
533
  // --no-mouse
534
  cmd_map['m'] = [opt] (const auto&) { opt().mouse_support = false; };
2✔
535
  // --no-optimized-cursor
536
  cmd_map['o'] = [opt] (const auto&) { opt().cursor_optimisation = false; };
2✔
537
  // --no-terminal-detection
538
  cmd_map['d'] = [opt] (const auto&) { opt().terminal_detection = false; };
2✔
539
  // --no-terminal-data-request
540
  cmd_map['r'] = [opt] (const auto&) { opt().terminal_data_request = false; };
2✔
541
  // --no-terminal-focus-events
542
  cmd_map['f'] = [opt] (const auto&) { opt().terminal_focus_events = false; };
2✔
543
  // --no-color-change
544
  cmd_map['c'] = [opt] (const auto&) { opt().color_change = false; };
2✔
545
  // --no-sgr-optimizer
546
  cmd_map['s'] = [opt] (const auto&) { opt().sgr_optimizer = false; };
2✔
547
  // --vgafont
548
  cmd_map['v'] = [opt] (const auto&) { opt().vgafont = true; };
2✔
549
  // --newfont
550
  cmd_map['n'] = [opt] (const auto&) { opt().newfont = true; };
2✔
551
  // --dark-theme
552
  cmd_map['t'] = [opt] (const auto&) { opt().dark_theme = true; };
2✔
553
#if defined(__FreeBSD__) || defined(__DragonFly__)
554
  // --no-esc-for-alt-meta
555
  cmd_map['E'] = [opt] (const auto&) { opt().meta_sends_escape = false; };
556
  // --no-cursorstyle-change
557
  cmd_map['C'] = [opt] (const auto&) { opt().change_cursorstyle = false; };
558
#elif defined(__NetBSD__) || defined(__OpenBSD__)
559
  // --no-esc-for-alt-meta
560
  cmd_map['E'] = [opt] (const auto&) { opt().meta_sends_escape = false; };
561
#endif
562
}
2✔
563

564
//----------------------------------------------------------------------
565
void FApplication::cmdOptions (const Args& args)
2✔
566
{
567
  // Interpret the command line options
568

569
  CmdMap cmd_map{};
2✔
570
  setCmdOptionsMap(cmd_map);
2✔
571
  auto argc = args.size();
2✔
572
  std::vector<const char*> argv(argc);
2✔
573
  std::transform ( args.cbegin()
2✔
574
                 , args.cend()
575
                 , argv.begin()
576
                 , [] (const auto& str)
1✔
577
                   {
578
                     return str.data();
1✔
579
                   }
580
                 );
581

582
  while ( true )
583
  {
584
    opterr = 0;
2✔
585
    int idx{0};
2✔
586
    const auto& long_options = getLongOptions();
2✔
587
    const struct option* p = long_options.data();
2✔
588
    auto argv_data = const_cast<char* const*>(argv.data());
2✔
589
    const int opt = getopt_long (int(argc), argv_data, "", p, &idx);
2✔
590

591
    if ( opt == -1 )
2✔
592
      break;
2✔
593

594
    const auto& iter = cmd_map.find(opt);
×
595

596
    if ( iter != cmd_map.end() )
×
597
      iter->second(optarg);
×
598
  }
×
599

600
  cmd_map.clear();
2✔
601
}
2✔
602

603
//----------------------------------------------------------------------
604
inline auto FApplication::getStartOptions() -> FStartOptions&
8✔
605
{
606
  static auto& start_options = FStartOptions::getInstance();
8✔
607
  return start_options;
8✔
608
}
609

610
//----------------------------------------------------------------------
611
void FApplication::showParameterUsage()
×
612
{
613
  std::cout \
614
    << "Generic options:\n"
615
    << "  -h, --help                "
616
    << "    Display this help and exit\n"
617
    << "\n"
618
    << "FINAL CUT options:\n"
619
    << "  --encoding=<MODE>         "
620
    << "    Sets the character encoding mode\n"
621
    << "                            "
622
    << "    {utf8, vt100, pc, ascii}\n"
623
    << "  --log-file=<FILE>         "
624
    << "    Writes log output to FILE\n"
625
    << "  --no-mouse                "
626
    << "    Disable mouse support\n"
627
    << "  --no-optimized-cursor     "
628
    << "    Disable cursor optimization\n"
629
    << "  --no-terminal-detection   "
630
    << "    Disable terminal detection\n"
631
    << "  --no-terminal-data-request"
632
    << "    Do not determine terminal font and title\n"
633
    << "  --no-terminal-focus-events"
634
    << "    Do not send focus-in and focus-out events\n"
635
    << "  --no-color-change         "
636
    << "    Do not redefine the color palette\n"
637
    << "  --no-sgr-optimizer        "
638
    << "    Do not optimize SGR sequences\n"
639
    << "  --vgafont                 "
640
    << "    Set the standard vga 8x16 font\n"
641
    << "  --newfont                 "
642
    << "    Enables the graphical font\n"
643
    << "  --dark-theme              "
644
    << "    Enables the dark theme\n"
×
645

646
#if defined(__FreeBSD__) || defined(__DragonFly__)
647
    << "\n"
648
    << "FreeBSD console options:\n"
649
    << "  --no-esc-for-alt-meta     "
650
    << "    Do not send a ESC prefix for the alt/meta key\n"
651
    << "  --no-cursorstyle-change   "
652
    << "    Do not change the current cursor style\n"
653
#elif defined(__NetBSD__) || defined(__OpenBSD__)
654
    << "\n"
655
    << "NetBSD/OpenBSD console options:\n"
656
    << "  --no-esc-for-alt-meta     "
657
    << "    Do not send a ESC prefix for the alt/meta key\n"
658
#endif
659

660
    << std::endl;  // newline character + flushes the output stream
×
661
}
×
662

663
//----------------------------------------------------------------------
664
inline void FApplication::resetLog() const
2✔
665
{
666
  // Reset the rdbuf of clog
667
  std::clog << std::flush;
2✔
668
  std::clog.rdbuf(default_clog_rdbuf);
2✔
669
}
2✔
670

671
//----------------------------------------------------------------------
672
inline void FApplication::findKeyboardWidget() const
×
673
{
674
  // Find the widget that has the keyboard focus
675

676
  FWidget* widget{nullptr};
×
677
  auto focus = getFocusWidget();
×
678
  auto move_size = getMoveResizeWidget();
×
679

680
  if ( focus )
×
681
  {
682
    widget = move_size ? move_size : focus;
×
683
  }
684
  else
685
  {
686
    widget = getMainWidget();
×
687

688
    if ( widget && widget->numOfFocusableChildren() >= 1 )
×
689
      widget->focusFirstChild();
×
690
  }
691

692
  keyboard_widget = widget;
×
693
}
×
694

695
//----------------------------------------------------------------------
696
inline auto FApplication::isKeyPressed (uInt64 ms) const -> bool
×
697
{
698
  static auto& mouse = FMouseControl::getInstance();
×
699
  static auto& keyboard = FKeyboard::getInstance();
×
700
  const auto blocking_time = (ms != 0U) ? ms : keyboard.getReadBlockingTime();
×
701

702
  if ( mouse.isGpmMouseEnabled() )
×
703
    return mouse.getGpmKeyPressed(keyboard.hasUnprocessedInput());
×
704

705
  return (keyboard.isKeyPressed(blocking_time) || keyboard.hasPendingInput());
×
706
}
707

708
//----------------------------------------------------------------------
709
void FApplication::keyPressed()
×
710
{
711
  performKeyboardAction();
×
712
}
×
713

714
//----------------------------------------------------------------------
715
void FApplication::keyReleased() const
×
716
{
717
  sendKeyUpEvent (keyboard_widget);
×
718
}
×
719

720
//----------------------------------------------------------------------
721
void FApplication::escapeKeyPressed() const
×
722
{
723
  sendEscapeKeyPressEvent();
×
724
}
×
725

726
//----------------------------------------------------------------------
727
void FApplication::mouseTracking() const
×
728
{
729
  performMouseAction();
×
730
}
×
731

732
//----------------------------------------------------------------------
733
inline void FApplication::performKeyboardAction()
×
734
{
735
  static const auto& keyboard = FKeyboard::getInstance();
×
736

737
  if ( keyboard.getKey() == FKey::Ctrl_l )  // Ctrl-L (redraw the screen)
×
738
  {
739
    redraw();
×
740
  }
741
  else if ( keyboard.getKey() == FKey::Term_Focus_In
×
742
         || keyboard.getKey() == FKey::Term_Focus_Out )
×
743
  {
744
    processTerminalFocus (keyboard.getKey());  // Term focus-in/focus-out
×
745
  }
746
  else
747
  {
748
    const bool acceptKeyDown = sendKeyDownEvent (keyboard_widget);
×
749
    const bool acceptKeyPress = sendKeyPressEvent (keyboard_widget);
×
750

751
    if ( ! (acceptKeyDown || acceptKeyPress) )
×
752
      sendKeyboardAccelerator();
×
753
  }
754
}
×
755

756
//----------------------------------------------------------------------
757
inline void FApplication::performMouseAction() const
×
758
{
759
  static auto& mouse = FMouseControl::getInstance();
×
760
  static auto& keyboard = FKeyboard::getInstance();
×
761
  const auto key = keyboard.getKey();
×
762
  auto& buffer = keyboard.getKeyBuffer();
×
763

764
  if ( key == FKey::X11mouse )
×
765
  {
766
    mouse.setRawData (FMouse::MouseType::X11, buffer);
×
767
  }
768
  else if ( key == FKey::Extended_mouse )
×
769
  {
770
    mouse.setRawData (FMouse::MouseType::Sgr, buffer);
×
771
  }
772
  else if ( key == FKey::Urxvt_mouse )
×
773
  {
774
    mouse.setRawData (FMouse::MouseType::Urxvt, buffer);
×
775
  }
776

777
  queuingMouseInput();
×
778
}
×
779

780
//----------------------------------------------------------------------
781
void FApplication::mouseEvent (const FMouseData& md) const
×
782
{
783
  for (const auto& mouse_handler : mouse_handler_list)
×
784
    mouse_handler(md);  // Execute mouse handler
×
785

786
  sendMouseEvent (md);
×
787
  sendMouseWheelEvent (md);
×
788
}
×
789

790
//----------------------------------------------------------------------
791
inline void FApplication::sendEscapeKeyPressEvent() const
×
792
{
793
  // Send an escape key press event
794
  FKeyEvent k_press_ev (Event::KeyPress, FKey::Escape);
×
795
  sendEvent (keyboard_widget, &k_press_ev);
×
796
}
×
797

798
//----------------------------------------------------------------------
799
inline auto FApplication::sendKeyDownEvent (FWidget* widget) const -> bool
×
800
{
801
  // Send key down event
802
  static const auto& keyboard = FKeyboard::getInstance();
×
803
  FKeyEvent k_down_ev (Event::KeyDown, keyboard.getKey());
×
804
  sendEvent (widget, &k_down_ev);
×
805
  return k_down_ev.isAccepted();
×
806
}
807

808
//----------------------------------------------------------------------
809
inline auto FApplication::sendKeyPressEvent (FWidget* widget) const -> bool
×
810
{
811
  // Send key press event
812
  static const auto& keyboard = FKeyboard::getInstance();
×
813
  FKeyEvent k_press_ev (Event::KeyPress, keyboard.getKey());
×
814
  sendEvent (widget, &k_press_ev);
×
815
  return k_press_ev.isAccepted();
×
816
}
817

818
//----------------------------------------------------------------------
819
inline auto FApplication::sendKeyUpEvent (FWidget* widget) const -> bool
×
820
{
821
  // Send key up event
822
  static const auto& keyboard = FKeyboard::getInstance();
×
823
  FKeyEvent k_up_ev (Event::KeyUp, keyboard.getKey());
×
824
  sendEvent (widget, &k_up_ev);
×
825
  return k_up_ev.isAccepted();
×
826
}
827

828
//----------------------------------------------------------------------
829
inline void FApplication::sendKeyboardAccelerator() const
×
830
{
831
  if ( FWidget::getOpenMenu() )
×
832
    return;
×
833

834
  // Switch to a specific dialog with Meta + 1..9
835
  bool accpt = processDialogSwitchAccelerator();
×
836

837
  // Windows keyboard accelerator
838
  if ( ! accpt )
×
839
  {
840
    auto window = static_cast<const FWidget*>(getActiveWindow());
×
841

842
    if ( window )
×
843
      accpt = processAccelerator(*window);
×
844
  }
845

846
  // Global keyboard accelerator
847
  if ( ! accpt )
×
848
  {
849
    auto root_widget = getRootWidget();
×
850

851
    if ( root_widget )
×
852
      processAccelerator(*root_widget);
×
853
  }
854
}
855

856
//----------------------------------------------------------------------
857
inline auto FApplication::hasDataInQueue() const -> bool
×
858
{
859
  static const auto& keyboard = FKeyboard::getInstance();
×
860
  static const auto& mouse = FMouseControl::getInstance();
×
861
  return ( keyboard.hasDataInQueue() || mouse.hasDataInQueue() );
×
862
}
863

864
//----------------------------------------------------------------------
865
void FApplication::queuingKeyboardInput() const
×
866
{
867
  findKeyboardWidget();
×
868
  static auto& keyboard = FKeyboard::getInstance();
×
869
  keyboard.escapeKeyHandling();  // special case: Esc key
×
870
  keyboard.clearKeyBufferOnTimeout();
×
871

872
  if ( isKeyPressed() )
×
873
    keyboard.fetchKeyCode();
×
874
}
×
875

876
//----------------------------------------------------------------------
877
void FApplication::queuingMouseInput() const
×
878
{
879
  static auto& mouse = FMouseControl::getInstance();
×
880

881
  if ( ! mouse.hasData() )
×
882
    return;
×
883

884
  static const auto& keyboard = FKeyboard::getInstance();
×
885
  auto time_keypressed = keyboard.getKeyPressedTime();
×
886
  mouse.processEvent (time_keypressed);
×
887
  mouse.clearEvent();
×
888
}
889

890
//----------------------------------------------------------------------
891
void FApplication::processKeyboardEvent() const
×
892
{
893
  static auto& keyboard = FKeyboard::getInstance();
×
894
  keyboard.processQueuedInput();
×
895
}
×
896

897
//----------------------------------------------------------------------
898
void FApplication::processMouseEvent() const
×
899
{
900
  static auto& mouse = FMouseControl::getInstance();
×
901
  mouse.processQueuedInput();
×
902
}
×
903

904
//----------------------------------------------------------------------
905
inline void FApplication::processInput() const
×
906
{
907
  if ( quit_now || internal::var::exit_loop || has_terminal_resized )
×
908
    return;
×
909

910
  // Keyboard and mouse raw data processing
911
  queuingKeyboardInput();
×
912
  queuingMouseInput();
×
913

914
  do
915
  {
916
    // Processing of captured keyboard and mouse events
917
    processKeyboardEvent();
×
918
    processMouseEvent();
×
919
  }
920
  while ( ! quit_now && hasDataInQueue() );
×
921
  // quit_now is checked again to avoid an infinite loop on exit
922
}
923

924
//----------------------------------------------------------------------
925
auto FApplication::processDialogSwitchAccelerator() const -> bool
×
926
{
927
  static const auto& keyboard = FKeyboard::getInstance();
×
928

929
  if ( keyboard.getKey() >= FKey::Meta_1
×
930
    && keyboard.getKey() <= FKey::Meta_9 )
×
931
  {
932
    const FKey key = keyboard.getKey();
×
933
    const auto n = std::size_t(key - FKey::Meta_0);
×
934
    const std::size_t s = getDialogList()->size();
×
935

936
    if ( s > 0 && s >= n )
×
937
    {
938
      // unset the move/resize mode
939
      auto move_size = getMoveResizeWidget();
×
940

941
      if ( move_size )
×
942
      {
943
        auto w = move_size;
×
944
        setMoveSizeWidget(nullptr);
×
945
        w->redraw();
×
946
      }
947

948
      FAccelEvent a_ev (Event::Accelerator, getFocusWidget());
×
949
      sendEvent (getDialogList()->at(n - 1), &a_ev);
×
950
      return true;
×
951
    }
952
  }
953

954
  return false;
×
955
}
956

957
//----------------------------------------------------------------------
958
auto FApplication::processAccelerator (const FWidget& widget) const -> bool
×
959
{
960
  if ( widget.getAcceleratorList().empty() )
×
961
    return false;
×
962

963
  for (auto&& item : widget.getAcceleratorList())
×
964
  {
965
    static const auto& keyboard = FKeyboard::getInstance();
×
966

967
    if ( item.key == keyboard.getKey() )
×
968
    {
969
      // unset the move/size mode
970
      auto move_size = getMoveResizeWidget();
×
971

972
      if ( move_size )
×
973
      {
974
        setMoveSizeWidget(nullptr);
×
975
        move_size->redraw();
×
976
      }
977

978
      FAccelEvent a_ev (Event::Accelerator, getFocusWidget());
×
979
      sendEvent (item.object, &a_ev);
×
980
      return a_ev.isAccepted();
×
981
    }
982

983
    if ( quit_now || internal::var::exit_loop )
×
984
      break;
985
  }
986

987
  return false;
×
988
}
989

990
//----------------------------------------------------------------------
991
void FApplication::processTerminalFocus (const FKey& key) const
×
992
{
993
  auto root_widget = getRootWidget();
×
994

995
  if ( ! root_widget
×
996
    || ( key != FKey::Term_Focus_In && key != FKey::Term_Focus_Out ) )
×
997
    return;
×
998

999
  // unset the move/size mode
1000
  auto move_size = getMoveResizeWidget();
×
1001

1002
  if ( move_size )
×
1003
  {
1004
    setMoveSizeWidget(nullptr);
×
1005
    move_size->redraw();
×
1006
  }
1007

1008
  auto event = ( key == FKey::Term_Focus_In )
×
1009
              ? Event::TerminalFocusIn
×
1010
              : Event::TerminalFocusOut;
1011
  FFocusEvent tf_ev (event);
×
1012
  sendEvent (root_widget, &tf_ev);
×
1013
}
1014

1015
//----------------------------------------------------------------------
1016
void FApplication::determineClickedWidget (const FMouseData& md)
×
1017
{
1018
  if ( isWheelEvent(md) )
×
1019
    return;
×
1020

1021
  clicked_widget = FWidget::getClickedWidget();
×
1022

1023
  // Check if the clicked widget has already been found
1024
  // or if the mouse has a non-press or wheel event
1025
  if ( clicked_widget || isNonActivatingMouseEvent(md) )
×
1026
    return;
×
1027

1028
  const auto& mouse_position = md.getPos();
×
1029

1030
  // Determine the window object on the current click position
1031
  auto window = FWindow::getWindowWidgetAt (mouse_position);
×
1032

1033
  if ( ! window )
×
1034
    return;
×
1035

1036
  // Determine the widget at the current click position
1037
  auto child = window->childWidgetAt(mouse_position);
×
1038
  clicked_widget = ( child != nullptr ) ? child : window;
×
1039
  setClickedWidget (clicked_widget);
×
1040
}
1041

1042
//----------------------------------------------------------------------
1043
void FApplication::determineWheelWidget (const FMouseData& md)
×
1044
{
1045
  if ( ! isWheelEvent(md) )
×
1046
    return;
×
1047

1048
  const auto& mouse_position = md.getPos();
×
1049

1050
  // Determine the window object on the current click position
1051
  auto window = FWindow::getWindowWidgetAt (mouse_position);
×
1052

1053
  if ( ! window )
×
1054
    return;
×
1055

1056
  // Determine the widget at the current click position
1057
  auto child = window->childWidgetAt(mouse_position);
×
1058
  wheel_widget = ( child != nullptr ) ? child : window;
×
1059
}
1060

1061
//----------------------------------------------------------------------
1062
auto FApplication::isNonActivatingMouseEvent (const FMouseData& md) -> bool
×
1063
{
1064
  return md.isLeftButtonReleased()
×
1065
      || md.isRightButtonReleased()
×
1066
      || md.isMiddleButtonReleased()
×
1067
      || md.isShiftKeyPressed()
×
1068
      || md.isControlKeyPressed()
×
1069
      || md.isMetaKeyPressed()
×
1070
      || md.isMoved();
×
1071
}
1072

1073
//----------------------------------------------------------------------
1074
auto FApplication::isWheelEvent (const FMouseData& md) -> bool
×
1075
{
1076
  return md.isWheelUp()
×
1077
      || md.isWheelDown()
×
1078
      || md.isWheelLeft()
×
1079
      || md.isWheelRight();
×
1080
}
1081

1082
//----------------------------------------------------------------------
1083
void FApplication::unsetMoveResizeMode (const FMouseData&)
×
1084
{
1085
  // Unset the move or resize mode
1086

1087
  auto& move_size = getMoveResizeWidget();
×
1088

1089
  if ( ! move_size )
×
1090
    return;
×
1091

1092
  FWidget* w{nullptr};
×
1093
  std::swap(w, move_size);  // Clear move_resize_widget
×
1094
  w->redraw();
×
1095
}
1096

1097
//----------------------------------------------------------------------
1098
void FApplication::sendMouseEvent (const FMouseData& md) const
×
1099
{
1100
  if ( ! FWidget::getClickedWidget() || wheel_widget )  // No widget was clicked
×
1101
    return;
×
1102

1103
  const auto& mouse_position = md.getPos();
×
1104
  MouseButton key_state{MouseButton::None};
×
1105

1106
  if ( md.isShiftKeyPressed() )
×
1107
    key_state |= MouseButton::Shift;
×
1108

1109
  if ( md.isControlKeyPressed() )
×
1110
    key_state |= MouseButton::Control;
×
1111

1112
  if ( md.isMetaKeyPressed() )
×
1113
    key_state |= MouseButton::Meta;
×
1114

1115
  const auto& widget_mouse_pos = clicked_widget->termToWidgetPos(mouse_position);
×
1116

1117
  if ( md.isMoved() )
×
1118
  {
1119
    sendMouseMoveEvent (md, widget_mouse_pos, mouse_position, key_state);
×
1120
  }
1121
  else
1122
  {
1123
    sendMouseLeftClickEvent (md, widget_mouse_pos, mouse_position, key_state);
×
1124
    sendMouseRightClickEvent (md, widget_mouse_pos, mouse_position, key_state);
×
1125
    sendMouseMiddleClickEvent (md, widget_mouse_pos, mouse_position, key_state);
×
1126
  }
1127
}
1128

1129
//----------------------------------------------------------------------
1130
void FApplication::sendMouseWheelEvent (const FMouseData& md)const
×
1131
{
1132
  if ( ! wheel_widget )  // No widget under the mouse pointer
×
1133
    return;
×
1134

1135
  const auto& mouse_position = md.getPos();
×
1136
  const auto& widget_mouse_pos = wheel_widget->termToWidgetPos(mouse_position);
×
1137
  sendWheelEvent (md, widget_mouse_pos, mouse_position);
×
1138
}
1139

1140
//----------------------------------------------------------------------
1141
void FApplication::sendMouseMoveEvent ( const FMouseData& md
×
1142
                                      , const FPoint& widget_mouse_pos
1143
                                      , const FPoint& mouse_position
1144
                                      , MouseButton key_state ) const
1145
{
1146
  if ( md.isLeftButtonPressed() )
×
1147
  {
1148
    FMouseEvent m_down_ev ( Event::MouseMove
1149
                          , widget_mouse_pos
1150
                          , mouse_position
1151
                          , MouseButton::Left | key_state );
×
1152
    sendEvent (clicked_widget, &m_down_ev);
×
1153
  }
1154

1155
  if ( md.isRightButtonPressed() )
×
1156
  {
1157
    FMouseEvent m_down_ev ( Event::MouseMove
1158
                          , widget_mouse_pos
1159
                          , mouse_position
1160
                          , MouseButton::Right | key_state );
×
1161
    sendEvent (clicked_widget, &m_down_ev);
×
1162
  }
1163

1164
  if ( md.isMiddleButtonPressed() )
×
1165
  {
1166
    FMouseEvent m_down_ev ( Event::MouseMove
1167
                          , widget_mouse_pos
1168
                          , mouse_position
1169
                          , MouseButton::Middle | key_state );
×
1170
    sendEvent (clicked_widget, &m_down_ev);
×
1171
  }
1172
}
×
1173

1174
//----------------------------------------------------------------------
1175
void FApplication::sendMouseLeftClickEvent ( const FMouseData& md
×
1176
                                           , const FPoint& widget_mouse_pos
1177
                                           , const FPoint& mouse_position
1178
                                           , MouseButton key_state ) const
1179
{
1180
  if ( md.isLeftButtonDoubleClick() )
×
1181
  {
1182
    FMouseEvent m_dblclick_ev ( Event::MouseDoubleClick
1183
                              , widget_mouse_pos
1184
                              , mouse_position
1185
                              , MouseButton::Left | key_state );
×
1186
    sendEvent (clicked_widget, &m_dblclick_ev);
×
1187
  }
1188
  else if ( md.isLeftButtonPressed() )
×
1189
  {
1190
    FMouseEvent m_down_ev ( Event::MouseDown
1191
                          , widget_mouse_pos
1192
                          , mouse_position
1193
                          , MouseButton::Left | key_state );
×
1194
    sendEvent (clicked_widget, &m_down_ev);
×
1195
  }
1196
  else if ( md.isLeftButtonReleased() )
×
1197
  {
1198
    FMouseEvent m_up_ev ( Event::MouseUp
1199
                        , widget_mouse_pos
1200
                        , mouse_position
1201
                        , MouseButton::Left | key_state );
×
1202
    auto released_widget = clicked_widget;
×
1203

1204
    if ( ! md.isRightButtonPressed()
×
1205
      && ! md.isMiddleButtonPressed() )
×
1206
      setClickedWidget(nullptr);
×
1207

1208
    sendEvent (released_widget, &m_up_ev);
×
1209
  }
1210
}
×
1211

1212
//----------------------------------------------------------------------
1213
void FApplication::sendMouseRightClickEvent ( const FMouseData& md
×
1214
                                            , const FPoint& widget_mouse_pos
1215
                                            , const FPoint& mouse_position
1216
                                            , MouseButton key_state ) const
1217
{
1218
  if ( md.isRightButtonPressed() )
×
1219
  {
1220
    FMouseEvent m_down_ev ( Event::MouseDown
1221
                          , widget_mouse_pos
1222
                          , mouse_position
1223
                          , MouseButton::Right | key_state );
×
1224
    sendEvent (clicked_widget, &m_down_ev);
×
1225
  }
1226
  else if ( md.isRightButtonReleased() )
×
1227
  {
1228
    FMouseEvent m_up_ev ( Event::MouseUp
1229
                        , widget_mouse_pos
1230
                        , mouse_position
1231
                        , MouseButton::Right | key_state );
×
1232
    auto released_widget = clicked_widget;
×
1233

1234
    if ( ! md.isLeftButtonPressed()
×
1235
      && ! md.isMiddleButtonPressed() )
×
1236
      setClickedWidget(nullptr);
×
1237

1238
    sendEvent (released_widget, &m_up_ev);
×
1239
  }
1240
}
×
1241

1242
//----------------------------------------------------------------------
1243
void FApplication::sendMouseMiddleClickEvent ( const FMouseData& md
×
1244
                                             , const FPoint& widget_mouse_pos
1245
                                             , const FPoint& mouse_position
1246
                                             , MouseButton key_state ) const
1247
{
1248
  if ( md.isMiddleButtonPressed() )
×
1249
  {
1250
    FMouseEvent m_down_ev ( Event::MouseDown
1251
                          , widget_mouse_pos
1252
                          , mouse_position
1253
                          , MouseButton::Middle | key_state );
×
1254
    sendEvent (clicked_widget, &m_down_ev);
×
1255
  }
1256
  else if ( md.isMiddleButtonReleased() )
×
1257
  {
1258
    FMouseEvent m_up_ev ( Event::MouseUp
1259
                        , widget_mouse_pos
1260
                        , mouse_position
1261
                        , MouseButton::Middle | key_state );
×
1262
    auto released_widget = clicked_widget;
×
1263

1264
    if ( ! md.isLeftButtonPressed()
×
1265
      && ! md.isRightButtonPressed() )
×
1266
    {
1267
      setClickedWidget(nullptr);
×
1268
    }
1269

1270
    sendEvent (released_widget, &m_up_ev);
×
1271
  }
1272
}
×
1273

1274
//----------------------------------------------------------------------
1275
void FApplication::sendWheelEvent ( const FMouseData& md
×
1276
                                  , const FPoint& widget_mouse_pos
1277
                                  , const FPoint& mouse_position ) const
1278
{
1279
  if ( ! isWheelEvent(md) )
×
1280
    return;
×
1281

1282
  auto mouse_wheel = [&md] ()
×
1283
  {
1284
    if ( md.isWheelUp() )
×
1285
      return MouseWheel::Up;
×
1286

1287
    if ( md.isWheelDown() )
×
1288
      return MouseWheel::Down;
×
1289

1290
    if ( md.isWheelLeft() )
×
1291
      return MouseWheel::Left;
×
1292

1293
    if ( md.isWheelRight() )
×
1294
      return MouseWheel::Right;
×
1295

1296
    return MouseWheel::None;
×
1297
  }();
×
1298

1299
  FWheelEvent wheel_ev ( Event::MouseWheel
1300
                       , widget_mouse_pos
1301
                       , mouse_position
1302
                       , mouse_wheel );
×
1303
  auto scroll_over_widget = wheel_widget;
×
1304
  sendEvent (scroll_over_widget, &wheel_ev);
×
1305
  wheel_widget = nullptr;
×
1306
}
1307

1308
//----------------------------------------------------------------------
1309
auto FApplication::processParameters (const Args& args) -> FWidget*
2✔
1310
{
1311
  // Save the default clog stream buffer
1312
  default_clog_rdbuf = std::clog.rdbuf();
2✔
1313

1314
  if ( args.size() > 1 && (args[1] == "--help" || args[1] == "-h") )
2✔
1315
  {
1316
    showParameterUsage();
×
1317
    FApplication::exit(EXIT_SUCCESS);
×
1318
  }
1319

1320
  getStartOptions().is_being_initialized = true;
2✔
1321
  cmdOptions (args);
2✔
1322
  getStartOptions().is_being_initialized = false;
2✔
1323
  return nullptr;
2✔
1324
}
1325

1326
//----------------------------------------------------------------------
1327
void FApplication::processResizeEvent()
×
1328
{
1329
  if ( ! has_terminal_resized )  // A SIGWINCH signal was received
×
1330
    return;
×
1331

1332
  auto foutput_ptr = FVTerm::getFOutput();
×
1333
  foutput_ptr->detectTerminalSize();  // Detect and save the current terminal size
×
1334
  static auto& mouse = FMouseControl::getInstance();
×
1335
  mouse.setMaxWidth (uInt16(getDesktopWidth()));
×
1336
  mouse.setMaxHeight (uInt16(getDesktopHeight()));
×
1337
  FResizeEvent r_ev(Event::Resize);
×
1338
  sendEvent(internal::var::app_object, &r_ev);
×
1339
  has_terminal_resized = false;
×
1340

1341
  if ( r_ev.isAccepted() )
×
1342
    foutput_ptr->commitTerminalResize();
×
1343
}
×
1344

1345
//----------------------------------------------------------------------
1346
void FApplication::processDialogResizeMove() const
×
1347
{
1348
  for (auto&& dialog : *FWidget::getDialogList())
×
1349
    dialog->flushChanges();
×
1350
}
×
1351

1352
//----------------------------------------------------------------------
1353
void FApplication::processCloseWidget()
×
1354
{
1355
  if ( ! getWidgetCloseList() || getWidgetCloseList()->empty() )
×
1356
    return;
×
1357

1358
  setTerminalUpdates (FVTerm::TerminalUpdate::Stop);
×
1359
  auto iter = getWidgetCloseList()->cbegin();
×
1360

1361
  while ( iter != getWidgetCloseList()->cend() && *iter )
×
1362
  {
1363
    delete *iter;
×
1364
    ++iter;
×
1365
  }
1366

1367
  getWidgetCloseList()->clear();
×
1368
  setTerminalUpdates (FVTerm::TerminalUpdate::Start);
×
1369
}
1370

1371
//----------------------------------------------------------------------
1372
void FApplication::processLogger() const
×
1373
{
1374
  // Synchronizing the stream buffer with the logging output
1375

1376
  const auto& logger = getLog();
×
1377

1378
  if ( ! logger )
×
1379
    return;
×
1380

1381
  if ( ! logger->str().empty() )
×
1382
    logger->pubsync();
×
1383

1384
  logger->flush();
×
1385
}
1386

1387
//----------------------------------------------------------------------
1388
auto FApplication::processNextEvent() -> bool
×
1389
{
1390
  uInt num_events{0};
×
1391

1392
  if ( hasDataInQueue() || hasTerminalResized() || isNextEventTimeout() )
×
1393
  {
1394
    time_last_event = FObjectTimer::getCurrentTime();
×
1395
    num_events += processTimerEvent();
×
1396
    processInput();
×
1397
    processResizeEvent();  // when the terminal size has changed
×
1398
    processCloseWidget();
×
1399
    sendQueuedEvents();
×
1400
    processDialogResizeMove();
×
1401
    processTerminalUpdate();  // for changed areas on the terminal
×
1402
    flush();  // Flush output buffer (via an instance of FOutput)
×
1403
    processLogger();
×
1404
  }
1405
  else if ( isKeyPressed(next_event_wait) )
×
1406
  {
1407
    time_last_event = TimeValue{};
×
1408
  }
1409

1410
  processExternalUserEvent();
×
1411
  return ( num_events > 0 );
×
1412
}
1413

1414
//----------------------------------------------------------------------
1415
void FApplication::performTimerAction (FObject* receiver, FEvent* event)
×
1416
{
1417
  sendEvent (receiver, event);
×
1418
}
×
1419

1420
//----------------------------------------------------------------------
1421
inline auto FApplication::hasTerminalResized() -> bool
×
1422
{
1423
  auto foutput_ptr = FVTerm::getFOutput();
×
1424
  has_terminal_resized = foutput_ptr->hasTerminalResized();
×
1425
  return has_terminal_resized;
×
1426
}
×
1427

1428
//----------------------------------------------------------------------
1429
auto FApplication::isEventProcessable ( FObject* receiver
121✔
1430
                                      , const FEvent* event ) -> bool
1431
{
1432
  if ( ! receiver->isWidget() )  // No restrictions for non-widgets
121✔
1433
    return true;
13✔
1434

1435
  auto widget = static_cast<FWidget*>(receiver);
108✔
1436

1437
  if ( getModalDialogCounter() > 0 )
108✔
1438
  {
1439
    const FWidget* window = widget->isWindowWidget()
×
1440
                          ? widget
×
1441
                          : FWindow::getWindowWidget(widget);
×
1442

1443
    // block events for widgets in non modal windows
1444
    if ( window
×
1445
      && ! window->getFlags().visibility.modal
×
1446
      && ! window->isMenuWidget() )
×
1447
    {
1448
      static constexpr std::array<const Event, 13> blocked_events
1449
      {{
1450
        Event::KeyPress,
1451
        Event::KeyUp,
1452
        Event::KeyDown,
1453
        Event::MouseDown,
1454
        Event::MouseUp,
1455
        Event::MouseDoubleClick,
1456
        Event::MouseWheel,
1457
        Event::MouseMove,
1458
        Event::FocusIn,
1459
        Event::FocusOut,
1460
        Event::ChildFocusIn,
1461
        Event::ChildFocusOut,
1462
        Event::Accelerator
1463
      }};
1464

1465
      if ( std::any_of( blocked_events.cbegin()
×
1466
                      , blocked_events.cend()
1467
                      , [&event](const auto& ev)
×
1468
                        {
1469
                          return ev == event->getType();
×
1470
                        } ) )
1471
      {
1472
        return false;
×
1473
      }
1474
    }
1475
  }
1476

1477
  // Throw away mouse events for disabled widgets
1478
  return ( event->getType() < Event::MouseDown
108✔
1479
        || event->getType() > Event::MouseMove
108✔
1480
        || widget->isEnabled() );
216✔
1481
}
1482

1483
//----------------------------------------------------------------------
1484
auto FApplication::isNextEventTimeout() -> bool
×
1485
{
1486
  return FObjectTimer::isTimeout(time_last_event, next_event_wait);
×
1487
}
1488

1489

1490
// Friend functions definition of FEvent
1491
//----------------------------------------------------------------------
1492
void setSend (FEvent& event, bool state)
121✔
1493
{
1494
  event.send = state;
121✔
1495
}
121✔
1496

1497
//----------------------------------------------------------------------
1498
void setQueued (FEvent& event, bool state)
×
1499
{
1500
  event.queued = state;
×
1501
}
×
1502

1503

1504
// FLog non-member operators
1505
//----------------------------------------------------------------------
1506
auto operator << (std::ostream& outstr, FLog::LogLevel l) -> std::ostream&
5✔
1507
{
1508
  const auto& output = FApplication::getLog();
5✔
1509

1510
  if ( ! output )
5✔
1511
    return outstr;
×
1512

1513
  try
1514
  {
1515
    *output << l;
5✔
1516
  }
1517
  catch (const std::invalid_argument&)
1✔
1518
  {
1519
    try
1520
    {
1521
      *output << FLog::LogLevel::Info;
1✔
1522
    }
1523
    catch (const std::invalid_argument&)  // Avoid being thrown again
×
1524
    {
1525
      *output;
×
1526
    }
×
1527
  }
1✔
1528

1529
  return outstr;
5✔
1530
}
1531

1532
}  // namespace finalcut
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2026 Coveralls, Inc