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

gansm / finalcut / #772

15 Feb 2026 12:08AM UTC coverage: 69.046% (-0.03%) from 69.073%
#772

push

travis-ci

gansm
More iterators and further code improvements

77 of 129 new or added lines in 8 files covered. (59.69%)

3 existing lines in 3 files now uncovered.

37632 of 54503 relevant lines covered (69.05%)

250.4 hits per line

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

85.71
/final/vterm/fvterm.cpp
1
/***********************************************************************
2
* fvterm.cpp - Virtual terminal implementation                         *
3
*                                                                      *
4
* This file is part of the FINAL CUT widget toolkit                    *
5
*                                                                      *
6
* Copyright 2016-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 <algorithm>
24
#include <numeric>
25
#include <string>
26
#include <unordered_set>
27
#include <vector>
28

29
#include "final/fapplication.h"
30
#include "final/fc.h"
31
#include "final/ftypes.h"
32
#include "final/output/tty/ftermoutput.h"
33
#include "final/util/flog.h"
34
#include "final/util/fpoint.h"
35
#include "final/util/frect.h"
36
#include "final/util/fsize.h"
37
#include "final/util/fsystem.h"
38
#include "final/vterm/fcolorpair.h"
39
#include "final/vterm/fstyle.h"
40
#include "final/vterm/fvterm.h"
41

42
namespace finalcut
43
{
44

45
namespace internal
46
{
47

48
constexpr auto getTransparentMask() noexcept -> uInt32
49
{
50
  FCharAttribute mask{};
51
  mask.transparent = true;
52
  mask.color_overlay = true;
53
  mask.inherit_background = true;
54
  return FCharAttribute_to_uInt32(mask);
55
}
56

57
constexpr auto getTransparent() noexcept -> uInt32
58
{
59
  FCharAttribute mask{};
60
  mask.transparent = true;
61
  return FCharAttribute_to_uInt32(mask);
62
}
63

64
constexpr auto getColorOverlay() noexcept -> uInt32
65
{
66
  FCharAttribute mask{};
67
  mask.color_overlay = true;
68
  return FCharAttribute_to_uInt32(mask);
69
}
70

71
constexpr auto getInheritBackground() noexcept -> uInt32
72
{
73
  FCharAttribute mask{};
74
  mask.inherit_background = true;
75
  return FCharAttribute_to_uInt32(mask);
76
}
77

78
constexpr auto getPrintTransparentMask() noexcept -> uInt32
79
{
80
  FCharAttribute mask{};
81
  mask.transparent = true;
82
  mask.color_overlay = true;
83
  mask.inherit_background = true;
84
  mask.no_changes = true;
85
  mask.printed = true;
86
  return FCharAttribute_to_uInt32(mask);
87
}
88

89
constexpr auto getResetMask() noexcept -> uInt32
90
{
91
  FCharAttribute mask{};
92
  mask.no_changes = true;
93
  mask.printed = true;
94
  return FCharAttribute_to_uInt32(mask);
95
}
96

97
constexpr auto getColorOverlayMask() noexcept -> uInt32
98
{
99
  FCharAttribute mask{};
100
  mask.color_overlay = true;
101
  mask.reverse = true;
102
  mask.standout = true;
103
  mask.no_changes = true;
104
  mask.printed = true;
105
  return FCharAttribute_to_uInt32(mask);
106
}
107

108
struct var
109
{
110
  static bool fvterm_initialized;  // Global init state
111
  static constexpr auto transparent_mask = getTransparentMask();
112
  static constexpr auto transparent = getTransparent();
113
  static constexpr auto color_overlay = getColorOverlay();
114
  static constexpr auto inherit_background = getInheritBackground();
115
  static constexpr auto print_transparent_mask = getPrintTransparentMask();
116
  static constexpr auto print_reset_mask = getResetMask();
117
  static constexpr auto color_overlay_mask = getColorOverlayMask();
118
};
119

120
bool             var::fvterm_initialized{false};
121
constexpr uInt32 var::transparent_mask;
122
constexpr uInt32 var::transparent;
123
constexpr uInt32 var::color_overlay;
124
constexpr uInt32 var::inherit_background;
125
constexpr uInt32 var::print_transparent_mask;
126
constexpr uInt32 var::print_reset_mask;
127
constexpr uInt32 var::color_overlay_mask;
128

129
}  // namespace internal
130

131
// static class attributes
132
bool                 FVTerm::draw_completed{false};
133
bool                 FVTerm::skip_one_vterm_update{false};
134
bool                 FVTerm::no_terminal_updates{false};
135
bool                 FVTerm::force_terminal_update{false};
136
FVTerm::FTermArea*   FVTerm::active_area{nullptr};
137
int                  FVTerm::tabstop{8};
138

139
using TransparentInvisibleLookupMap = std::unordered_set<wchar_t>;
140

141
//----------------------------------------------------------------------
142
static auto getTransparentInvisibleLookupMap() -> TransparentInvisibleLookupMap
1✔
143
{
144
  // Encapsulate global unordered_map object
145
  static const auto& trans_inv_lookup = std::make_unique<TransparentInvisibleLookupMap>
146
  (
147
    std::initializer_list<wchar_t>
1✔
148
    ({
149
      wchar_t(UniChar::LowerHalfBlock),
150
      wchar_t(UniChar::UpperHalfBlock),
151
      wchar_t(UniChar::LeftHalfBlock),
152
      wchar_t(UniChar::RightHalfBlock),
153
      wchar_t(UniChar::MediumShade),
154
      wchar_t(UniChar::FullBlock)
155
    })
156
  );
1✔
157

158
  return *trans_inv_lookup;
1✔
159
}
160

161
//----------------------------------------------------------------------
162
// class FVTerm
163
//----------------------------------------------------------------------
164

165
// constructors and destructor
166
//----------------------------------------------------------------------
167
FVTerm::FVTerm()
72✔
168
{
169
  init<FTermOutput>();
72✔
170
}
72✔
171

172
//----------------------------------------------------------------------
173
FVTerm::~FVTerm()  // destructor
80✔
174
{
175
  if ( getGlobalFVTermInstance() == this )
80✔
176
    finish();
32✔
177
}
80✔
178

179
//----------------------------------------------------------------------
180
auto FVTerm::operator << (FVTermBuffer& buffer) noexcept -> FVTerm&
2✔
181
{
182
  print (buffer);
2✔
183
  return *this;
2✔
184
}
185

186
//----------------------------------------------------------------------
187
auto FVTerm::operator << (const FVTermBuffer& buffer) noexcept -> FVTerm&
×
188
{
189
  print (buffer);
×
190
  return *this;
×
191
}
192

193

194
// public methods of FVTerm
195
//----------------------------------------------------------------------
196
auto FVTerm::getFOutput() -> std::shared_ptr<FOutput>
272✔
197
{
198
  static const auto& init_object = getGlobalFVTermInstance();
272✔
199
  return init_object->foutput;
272✔
200
}
201

202
//----------------------------------------------------------------------
203
auto FVTerm::getPrintCursor() -> FPoint
7✔
204
{
205
  const auto* win = getPrintArea();
7✔
206

207
  if ( win )
7✔
208
    return { win->position.x + win->cursor.x
7✔
209
           , win->position.y + win->cursor.y };
7✔
210

211
  return {0, 0};  // Fallback coordinates
×
212
}
213

214
//----------------------------------------------------------------------
215
void FVTerm::setTerminalUpdates (TerminalUpdate refresh_state) const
4✔
216
{
217
  if ( refresh_state == TerminalUpdate::Stop )
4✔
218
  {
219
    no_terminal_updates = true;
2✔
220
  }
221
  else if ( refresh_state == TerminalUpdate::Continue
2✔
222
         || refresh_state == TerminalUpdate::Start )
1✔
223
  {
224
    no_terminal_updates = false;
2✔
225
  }
226

227
  if ( refresh_state == TerminalUpdate::Start )
4✔
228
    updateTerminal();
1✔
229
}
4✔
230

231
//----------------------------------------------------------------------
232
void FVTerm::setCursor (const FPoint& pos) noexcept
55✔
233
{
234
  if ( auto* win = getPrintArea() )
55✔
235
  {
236
    win->setCursorPos ( pos.getX() - win->position.x
55✔
237
                      , pos.getY() - win->position.y );
55✔
238
  }
239
}
55✔
240

241
//----------------------------------------------------------------------
242
void FVTerm::setNonBlockingRead (bool enable)
4✔
243
{
244
  static const auto& init_object = getGlobalFVTermInstance();
4✔
245
  init_object->foutput->setNonBlockingRead (enable);
4✔
246
}
4✔
247

248
//----------------------------------------------------------------------
249
auto FVTerm::hasPreprocessingHandler (const FVTerm* instance) noexcept -> bool
2✔
250
{
251
  if ( ! print_area )
2✔
252
    getPrintArea();
1✔
253

254
  if ( ! instance || ! print_area || print_area->preproc_list.empty() )
2✔
255
    return false;
1✔
256

257
  auto iter = print_area->preproc_list.cbegin();
1✔
258

259
  while ( iter != print_area->preproc_list.cend() )
1✔
260
  {
261
    if ( iter->get()->instance.get() == instance )
1✔
262
      return true;
1✔
263

264
    ++iter;
×
265
  }
266

267
  return false;
×
268
}
269

270
//----------------------------------------------------------------------
271
void FVTerm::clearArea (wchar_t fillchar)
1✔
272
{
273
  clearArea (vwin.get(), fillchar);
1✔
274
}
1✔
275

276
//----------------------------------------------------------------------
277
void FVTerm::createVDesktop (const FSize& size) noexcept
33✔
278
{
279
  // Initialize virtual desktop area
280

281
  const FRect box{0, 0, size.getWidth(), size.getHeight()};
33✔
282
  vdesktop = createArea(box);
33✔
283
  vdesktop->visible = true;
33✔
284
}
33✔
285

286
//----------------------------------------------------------------------
287
void FVTerm::createVTerm (const FSize& size) noexcept
33✔
288
{
289
  // Initialize virtual terminal
290

291
  const FRect box{0, 0, size.getWidth(), size.getHeight()};
33✔
292
  vterm = createArea(box);
33✔
293
  vterm_old = createArea(box);
33✔
294
}
33✔
295

296
//----------------------------------------------------------------------
297
void FVTerm::resizeVTerm (const FSize& size) const noexcept
3✔
298
{
299
  // Resize virtual terminal
300

301
  const FRect box{0, 0, size.getWidth(), size.getHeight()};
3✔
302
  resizeArea (box, vterm.get());
3✔
303
  resizeArea (box, vterm_old.get());
3✔
304
}
3✔
305

306
//----------------------------------------------------------------------
307
void FVTerm::putVTerm() const
1✔
308
{
309
  // Update the entire terminal screen
310

311
  auto xmax = uInt(vterm->size.width - 1);
1✔
312
  auto ymax = uInt(vterm->size.height - 1);
1✔
313
  auto vterm_changes = vterm->changes_in_line.begin();
1✔
314
  std::for_each ( vterm_changes
1✔
315
                , vterm_changes + vterm->size.height
1✔
316
                , [&xmax] (auto& vterm_changes_line) noexcept
18✔
317
                  {
318
                    vterm_changes_line.xmin = 0;
18✔
319
                    vterm_changes_line.xmax = xmax;
18✔
320
                  } );
18✔
321
  vterm->changes_in_row = {0, ymax};
1✔
322
  updateTerminal();
1✔
323
}
1✔
324

325
//----------------------------------------------------------------------
326
auto FVTerm::updateTerminal() const -> bool
73✔
327
{
328
  // Update terminal screen when modified
329

330
  auto terminal_updated = canUpdateTerminalNow()
73✔
331
                        ? foutput->updateTerminal()
73✔
332
                        : false;
73✔
333

334
  if ( terminal_updated )
73✔
335
    saveCurrentVTerm();
1✔
336

337
  return terminal_updated;
73✔
338
}
339

340
//----------------------------------------------------------------------
341
void FVTerm::reduceTerminalLineUpdates (uInt y)
24✔
342
{
343
  static const auto& init_object = getGlobalFVTermInstance();
24✔
344
  static const auto& vterm = init_object->vterm;
24✔
345
  static const auto& vterm_old = init_object->vterm_old;
24✔
346
  auto& vterm_changes = vterm->changes_in_line[unsigned(y)];
24✔
347
  uInt& xmin = vterm_changes.xmin;
24✔
348
  uInt& xmax = vterm_changes.xmax;
24✔
349

350
  if ( xmin > xmax )  // No changes
24✔
351
    return;
×
352

353
  auto first = vterm->getFCharIterator(int(xmin), int(y));
24✔
354
  auto first_old = vterm_old->getFCharIterator(int(xmin), int(y));
24✔
355
  auto last = vterm->getFCharIterator(int(xmax), int(y));
24✔
356
  auto last_old = vterm_old->getFCharIterator(int(xmax), int(y));
24✔
357

358
  while ( xmin < xmax && *first == *first_old )
311✔
359
  {
360
    xmin++;
287✔
361
    ++first;
287✔
362
    ++first_old;
287✔
363
  }
364

365
  while ( last >= first && *last == *last_old )
63✔
366
  {
367
    xmax--;
39✔
368
    --last;
39✔
369
    --last_old;
39✔
370
  }
371

372
  while ( last > first )
52✔
373
  {
374
    if ( *last == *last_old )
28✔
375
      last->attr.bit.no_changes = true;
11✔
376

377
    --last;
28✔
378
    --last_old;
28✔
379
  }
380
}
381

382
//----------------------------------------------------------------------
383
void FVTerm::addPreprocessingHandler ( const FVTerm* instance
1✔
384
                                     , FPreprocessingFunction&& function )
385
{
386
  if ( ! print_area )
1✔
387
    getPrintArea();
×
388

389
  if ( ! instance || ! print_area )
1✔
390
    return;
×
391

392
  delPreprocessingHandler (instance);
1✔
393
  auto obj = std::make_unique<FVTermPreprocessing> \
394
      (instance, std::move(function));
1✔
395
  print_area->preproc_list.emplace_back(std::move(obj));
1✔
396
}
1✔
397

398
//----------------------------------------------------------------------
399
void FVTerm::delPreprocessingHandler (const FVTerm* instance)
2✔
400
{
401
  if ( ! print_area )
2✔
402
    getPrintArea();
×
403

404
  if ( ! instance || ! print_area || print_area->preproc_list.empty() )
2✔
405
    return;
1✔
406

407
  auto iter = print_area->preproc_list.cbegin();
1✔
408

409
  while ( iter != print_area->preproc_list.cend() )
2✔
410
  {
411
    if ( iter->get()->instance.get() == instance )
1✔
412
      iter = print_area->preproc_list.erase(iter);
1✔
413
    else
414
      ++iter;
×
415
  }
416
}
417

418
//----------------------------------------------------------------------
419
auto FVTerm::print (const FString& string) noexcept -> int
975✔
420
{
421
  if ( string.isEmpty() )
975✔
422
    return 0;
2✔
423

424
  vterm_buffer.print(string);
973✔
425
  return print (vterm_buffer);
973✔
426
}
427

428
//----------------------------------------------------------------------
429
auto FVTerm::print (FTermArea* area, const FString& string) noexcept -> int
4✔
430
{
431
  if ( ! area || string.isEmpty() )
4✔
432
    return -1;
2✔
433

434
  vterm_buffer.print(string);
2✔
435
  return print (area, vterm_buffer);
2✔
436
}
437

438
//----------------------------------------------------------------------
439
auto FVTerm::print (const std::vector<FChar>& term_string) noexcept -> int
6✔
440
{
441
  if ( term_string.empty() )
6✔
442
    return -1;
2✔
443

444
  vterm_buffer.assign(term_string.cbegin(), term_string.cend());
4✔
445
  return print (vterm_buffer);
4✔
446
}
447

448
//----------------------------------------------------------------------
449
auto FVTerm::print (FTermArea* area, const std::vector<FChar>& term_string) noexcept -> int
4✔
450
{
451
  if ( ! area || term_string.empty() )
4✔
452
    return -1;
2✔
453

454
  vterm_buffer.assign(term_string.cbegin(), term_string.cend());
2✔
455
  return print (area, vterm_buffer);
2✔
456
}
457

458
//----------------------------------------------------------------------
459
auto FVTerm::print (FVTermBuffer& buffer) noexcept -> int
981✔
460
{
461
  if ( buffer.isEmpty() )
981✔
462
    return -1;
2✔
463

464
  auto* area = getPrintArea();
979✔
465
  return area ? print (area, buffer) : -1;
979✔
466
}
467

468
//----------------------------------------------------------------------
469
auto FVTerm::print (const FVTermBuffer& buffer) noexcept -> int
×
470
{
471
  if ( buffer.isEmpty() )
×
472
    return -1;
×
473

474
  auto* area = getPrintArea();
×
475
  return area ? print (area, buffer) : -1;
×
476
}
477

478
//----------------------------------------------------------------------
479
auto FVTerm::print (FTermArea* area, FVTermBuffer& buffer) const noexcept -> int
987✔
480
{
481
  auto len = print (area, static_cast<const FVTermBuffer&>(buffer));
987✔
482
  buffer.clear();  // Clear FVTermBuffer after printing
987✔
483
  return len;
987✔
484
}
485

486
//----------------------------------------------------------------------
487
auto FVTerm::print (FTermArea* area, const FVTermBuffer& buffer) const noexcept -> int
987✔
488
{
489
  int len{0};
987✔
490

491
  if ( ! area || buffer.isEmpty() )
987✔
492
    return -1;
4✔
493

494
  auto ac = area->getFCharIterator( area->cursor.x - 1
983✔
495
                                  , area->cursor.y - 1 );  // area character iterator
983✔
496

497
  for (const auto& fchar : buffer)
4,405✔
498
  {
499
    if ( printCharacter(area, ac, fchar) == -1 )  // Print next character
3,432✔
500
      break;  // No area or end of area reached
10✔
501

502
    len++;
3,422✔
503
  }
504

505
  return len;
983✔
506
}
507

508
//----------------------------------------------------------------------
509
auto FVTerm::print (wchar_t c) noexcept -> int
10✔
510
{
511
  auto* area = getPrintArea();
10✔
512
  return area ? print (area, c) : -1;
10✔
513
}
514

515
//----------------------------------------------------------------------
516
auto FVTerm::print (FTermArea* area, wchar_t c) noexcept -> int
12✔
517
{
518
  if ( ! area )
12✔
519
    return -1;
2✔
520

521
  static const auto& next_attr = getAttribute();
10✔
522
  nc.color.data = next_attr.color.data;
10✔
523
  nc.attr.byte[0] = next_attr.attr.byte[0];
10✔
524
  nc.attr.byte[1] = next_attr.attr.byte[1];
10✔
525
  nc.attr.byte[2] = 0;
10✔
526
  nc.attr.byte[3] = 0;
10✔
527
  nc.ch[0] = c;
10✔
528
  nc.ch[1] = L'\0';
10✔
529
  return print (area, nc);
10✔
530
}
531

532
//----------------------------------------------------------------------
533
auto FVTerm::print (const FChar& term_char) noexcept -> int
4✔
534
{
535
  auto* area = getPrintArea();
4✔
536
  return area ? print (area, term_char) : -1;
4✔
537
}
538

539
//----------------------------------------------------------------------
540
auto FVTerm::print (FTermArea* area, const FChar& term_char) const noexcept -> int
18✔
541
{
542
  if ( ! area )
18✔
543
    return -1;  // No area
2✔
544

545
  auto ac = area->getFCharIterator( area->cursor.x - 1
16✔
546
                                  , area->cursor.y - 1 );  // area character iterator
16✔
547
  return printCharacter(area, ac, term_char);
16✔
548
}
549

550
//----------------------------------------------------------------------
551
void FVTerm::flush() const
44✔
552
{
553
  foutput->flush();
44✔
554
}
44✔
555

556

557
// protected methods of FVTerm
558
//----------------------------------------------------------------------
559
auto FVTerm::getPrintArea() -> FTermArea*
1,083✔
560
{
561
  // returns the print area of this object
562

563
  if ( print_area )
1,083✔
564
    return print_area;
1,064✔
565

566
  if ( vwin )
19✔
567
  {
568
    print_area = vwin.get();
16✔
569
    return print_area;
16✔
570
  }
571

572
  if ( child_print_area )
3✔
573
  {
574
    print_area = child_print_area;
×
575
    return print_area;
×
576
  }
577

578
  return vdesktop.get();
3✔
579
}
580

581
//----------------------------------------------------------------------
582
auto FVTerm::createArea (const FShadowBox& shadowbox) -> std::unique_ptr<FTermArea>
127✔
583
{
584
  // initialize virtual window
585

586
  auto area = std::make_unique<FTermArea>();
127✔
587
  area->setOwner<FVTerm*>(this);
127✔
588
  area->encoding = foutput->getEncoding();
127✔
589
  resizeArea (shadowbox, area.get());
127✔
590
  return area;
127✔
591
}
×
592

593
//----------------------------------------------------------------------
594
auto FVTerm::createArea (const FRect& box) -> std::unique_ptr<FTermArea>
121✔
595
{
596
  // initialize virtual window
597

598
  const auto no_shadow = FSize(0, 0);
121✔
599
  return createArea ({box, no_shadow});
121✔
600
}
601

602
//----------------------------------------------------------------------
603
void FVTerm::resizeArea ( const FShadowBox& shadowbox
139✔
604
                        , FTermArea* area ) const
605
{
606
  // Resize the virtual window to a new size
607

608
  assert ( isAreaValid(shadowbox) );
139✔
609

610
  if ( ! area )
139✔
611
    return;
×
612

613
  if ( isSizeEqual(area, shadowbox) )
139✔
614
  {
615
    area->position.x = shadowbox.box.getX();
5✔
616
    area->position.y = shadowbox.box.getY();
5✔
617
    return;  // Move only
5✔
618
  }
619

620
  const auto full_width  = shadowbox.box.getWidth()
134✔
621
                         + shadowbox.shadow.getWidth();
134✔
622
  const auto full_height = shadowbox.box.getHeight()
134✔
623
                         + shadowbox.shadow.getHeight();
134✔
624

625
  if ( ! tryResizeArea(area, full_width, full_height) )
134✔
626
    return;
×
627

628
  updateAreaProperties (area, shadowbox);
134✔
629
  // Set default FChar in area
630
  resetTextAreaToDefault (area, { FSize{full_width, full_height} });
134✔
631
}
632

633
//----------------------------------------------------------------------
634
void FVTerm::resizeArea (const FRect& box, FTermArea* area) const
10✔
635
{
636
  // Resize the virtual window to a new size
637

638
  const auto no_shadow = FSize(0, 0);
10✔
639
  resizeArea ({box, no_shadow}, area);
10✔
640
}
10✔
641

642
//----------------------------------------------------------------------
643
void FVTerm::restoreVTerm (const FRect& box) const noexcept
13✔
644
{
645
  if ( ! vterm || box.getWidth() == 0 || box.getHeight() == 0 )
13✔
646
    return;
×
647

648
  const auto* vterm_win_list = getWindowList();
13✔
649

650
  if ( ! vterm_win_list || vterm_win_list->empty() )
13✔
651
    return;
×
652

653
  const FSize vterm_size{ static_cast<std::size_t>(vterm->size.width)
13✔
654
                        , static_cast<std::size_t>(vterm->size.height) };
13✔
655

656
  if ( vdesktop && vdesktop->reprint(box, vterm_size) )
13✔
657
    addLayer(vdesktop.get());
13✔
658

659
  for (const auto& vterm_obj : *vterm_win_list)
44✔
660
  {
661
    if ( ! vterm_obj )
31✔
662
      continue;
×
663

664
    auto* win = vterm_obj->getVWin();
31✔
665

666
    if ( win && win->visible && win->layer > 0 && win->reprint(box, vterm_size) )
31✔
667
      addLayer(win);
27✔
668
  }
669
}
670

671
//----------------------------------------------------------------------
672
auto FVTerm::updateVTermCursor (const FTermArea* area) const noexcept -> bool
72✔
673
{
674
  if ( ! (area && isActive(area) && area->visible) )
72✔
675
    return false;
62✔
676

677
  if ( area->input_cursor_visible )
10✔
678
  {
679
    // area cursor position
680
    const int cx = area->input_cursor.x;
4✔
681
    const int cy = area->input_cursor.y;
4✔
682
    // terminal position = area position + area cursor position
683
    const int x  = area->position.x + cx;
4✔
684
    const int y  = area->position.y + cy;
4✔
685

686
    if ( isInsideArea (FPoint{cx, cy}, area)
4✔
687
      && isInsideTerminal (FPoint{x, y})
4✔
688
      && isCovered (FPoint{x, y}, area) == CoveredState::None )
8✔
689
    {
690
      vterm->setInputCursorPos(x, y);
3✔
691
      vterm->input_cursor_visible = true;
3✔
692
      vterm->has_changes = true;
3✔
693
      return true;
3✔
694
    }
695
  }
696

697
  vterm->input_cursor_visible = false;
7✔
698
  return false;
7✔
699
}
700

701
//----------------------------------------------------------------------
702
auto FVTerm::isCursorHideable() const noexcept -> bool
2✔
703
{
704
  return foutput->isCursorHideable();
2✔
705
}
706

707
//----------------------------------------------------------------------
708
void FVTerm::setAreaCursor ( const FPoint& pos
3✔
709
                           , bool visible
710
                           , FTermArea* area ) const noexcept
711
{
712
  if ( ! area )
3✔
713
    return;
×
714

715
  area->setInputCursorPos (pos.getX() - 1, pos.getY() - 1);
3✔
716
  area->input_cursor_visible = visible;
3✔
717
}
718

719
//----------------------------------------------------------------------
720
void FVTerm::getArea (const FPoint& pos, FTermArea* area) const noexcept
14✔
721
{
722
  // Copies a block from the virtual terminal position to the given area
723

724
  if ( ! area )
14✔
725
    return;
3✔
726

727
  const int ax = pos.getX() - 1;
14✔
728
  const int ay = pos.getY() - 1;
14✔
729

730
  if ( ax < 0 || ay < 0 )
14✔
731
    return;
3✔
732

733
  const int y_end  = std::min(vterm->size.height - ay, area->size.height);
11✔
734
  const int length = std::min(vterm->size.width - ax, area->size.width);
11✔
735
  const auto area_width = getFullAreaWidth(area);
11✔
736
  const auto vterm_width = getFullAreaWidth(vterm.get());
11✔
737
  auto line_changes = area->changes_in_line.begin();
11✔
738
  auto ac = area->data.begin();  // area character
11✔
739
  auto tc = vterm->data.cbegin() + (ay * vterm_width);  // terminal character
11✔
740

741
  for (auto y{0}; y < y_end; y++)  // line loop
50✔
742
  {
743
    putAreaLine (*(tc + unsigned(ax)), *ac, length);
39✔
744
    line_changes->xmin = 0;
39✔
745
    line_changes->xmax = uInt(length - 1);
39✔
746
    ++line_changes;
39✔
747
    ac += area_width;
39✔
748
    tc += vterm_width;
39✔
749
  }
750

751
  auto& changes_in_row = area->changes_in_row;
11✔
752
  changes_in_row.ymin = 0;
11✔
753
  changes_in_row.ymax = std::max(changes_in_row.ymax, uInt(y_end - 1));
11✔
754
}
755

756
//----------------------------------------------------------------------
757
void FVTerm::getArea (const FRect& box, FTermArea* area) const noexcept
13✔
758
{
759
  // Copies a block from the virtual terminal rectangle to the given area
760

761
  if ( ! area )
13✔
762
    return;
3✔
763

764
  int x = box.getX() - 1;
13✔
765
  int y = box.getY() - 1;
13✔
766
  int dx = x - area->position.x;
13✔
767
  int dy = y - area->position.y;
13✔
768
  auto w = std::min(area->size.width - dx, int(box.getWidth()));
13✔
769
  auto h = std::min(area->size.height - dy, int(box.getHeight()));
13✔
770

771
  if ( x < 0 || y < 0 )
13✔
772
    return;
3✔
773

774
  if ( dx < 0 ) { w += dx; x -= dx; dx = 0; }
10✔
775
  if ( dy < 0 ) { h += dy; y -= dy; dy = 0; }
10✔
776
  const int y_end = std::min(vterm->size.height - y, h);
10✔
777
  const int length = std::min(vterm->size.width - x, w);
10✔
778
  const auto area_width = getFullAreaWidth(area);
10✔
779
  const auto vterm_width = getFullAreaWidth(vterm.get());
10✔
780
  auto ac = area->data.begin() + (dy * area_width);  // area character
10✔
781
  auto tc = vterm->data.cbegin() + (y * vterm_width);  // terminal character
10✔
782

783
  if ( length < 1 )
10✔
784
    return;
×
785

786
  auto line_changes = area->changes_in_line.begin() + dy;
10✔
787

788
  for (auto line{0}; line < y_end; line++)  // line loop
42✔
789
  {
790
    putAreaLine (*(tc + x), *(ac + dx), length);
32✔
791
    line_changes->xmin = std::min(line_changes->xmin, uInt(dx));
32✔
792
    line_changes->xmax = std::max(line_changes->xmax, uInt(dx + length - 1));
32✔
793
    ++line_changes;
32✔
794
    ac += area_width;
32✔
795
    tc += vterm_width;
32✔
796
  }
797

798
  auto& changes_in_row = area->changes_in_row;
10✔
799
  changes_in_row.ymin = 0;
10✔
800
  changes_in_row.ymax = std::max(changes_in_row.ymax, uInt(y_end - 1));
10✔
801
}
802

803
//----------------------------------------------------------------------
804
void FVTerm::addLayer (FTermArea* area) const noexcept
91✔
805
{
806
  // Transmit changes in area to the virtual terminal (add layer to vterm)
807

808
  if ( ! area || ! area->visible )
91✔
809
    return;
19✔
810

811
  // Compute geometry and decide early whether to render
812
  LayerGeometry geometry = computeLayerGeometry(area);
88✔
813

814
  // Early exit if area is completely outside vterm
815
  if ( ! isLayerOutsideVTerm(geometry) )
88✔
816
    return;
×
817

818
  // Call the preprocessing handler methods (child area change handling)
819
  callPreprocessingHandler(area);
88✔
820

821
  // Create a stack by combining identical rows in sequence
822
  line_changes_batch.clear();  // Clear buffer
88✔
823
  buildLineChangeBatch(area, geometry);
88✔
824

825
  if ( line_changes_batch.empty() )
88✔
826
    return;
16✔
827

828
  // Apply the batches to vterm data
829
  applyLineBatch(area, geometry);
72✔
830

831
  // Update the range of changed rows and the cursor on vterm
832
  updateVTermChangesFromBatch(geometry);
72✔
833
  updateVTermCursor(area);
72✔
834
}
835

836
//----------------------------------------------------------------------
837
void FVTerm::putArea (const FPoint& pos, FTermArea* area) const noexcept
22✔
838
{
839
  // Copies the given area block to the virtual terminal position
840

841
  if ( ! area || ! area->visible )
22✔
842
    return;
1✔
843

844
  copyArea (vterm.get(), pos, area);
21✔
845
}
846

847
//----------------------------------------------------------------------
848
void FVTerm::copyArea (FTermArea* dst, const FPoint& pos, FTermArea* src) const noexcept
21✔
849
{
850
  // Copies the src area to the dst area position
851

852
  if ( ! dst || ! src )
21✔
853
    return;
3✔
854

855
  if ( dst == vterm.get() )
21✔
856
    skip_one_vterm_update = true;
21✔
857

858
  const int src_width = getFullAreaWidth(src);
21✔
859
  const int dst_width = getFullAreaWidth(dst);
21✔
860
  const int src_height = src->minimized ? src->min_size.height : getFullAreaHeight(src);
21✔
861
  const int ax = std::max(0, pos.getX() - 1);
21✔
862
  const int ay = std::max(0, pos.getY() - 1);
21✔
863
  const int ol = std::max(0, 1 - pos.getX());  // outside left
21✔
864
  const int ot = std::max(0, 1 - pos.getY());  // outside top
21✔
865
  const int y_end = std::min(dst->size.height - ay, src_height - ot);
21✔
866
  const int length = std::min(dst->size.width - ax, src_width - ol);
21✔
867

868
  if ( length < 1 || y_end < 1 )
21✔
869
    return;
3✔
870

871
  auto src_changes = src->changes_in_line.cbegin();
18✔
872
  auto dst_changes = dst->changes_in_line.begin() + ay;
18✔
873
  auto sc = src->data.cbegin() + (src_width * ot) + ol;  // src character ptr
18✔
874
  auto dc = dst->data.begin() + (dst_width * ay) + ax;  // dst character ptr
18✔
875

876
  if ( skip_one_vterm_update )  // dst is the virtual terminal
18✔
877
    determineCoveredAreas(src);
18✔
878

879
  for (int y{0}; y < y_end; y++)  // line loop
364✔
880
  {
881
    if ( skip_one_vterm_update && src_changes->trans_count > 0 )
346✔
882
    {
883
      // Line with hidden and transparent characters
884
      putAreaLineWithTransparency (sc, dc, length, {ax, ay + y}, src_changes->covered);
64✔
885
    }
886
    else
887
    {
888
      // Line has only covered characters
889
      putAreaLine (*sc, *dc, length);
282✔
890
    }
891

892
    dst_changes->xmin = std::min(dst_changes->xmin, uInt(ax));
346✔
893
    dst_changes->xmax = std::max(dst_changes->xmax, uInt(ax + length - 1));
346✔
894
    ++src_changes;
346✔
895
    ++dst_changes;
346✔
896
    sc += src_width;
346✔
897
    dc += dst_width;
346✔
898
  }
899

900
  dst->changes_in_row.ymin = std::min(dst->changes_in_row.ymin, uInt(ay));
18✔
901
  dst->changes_in_row.ymax = std::max(dst->changes_in_row.ymax, uInt(ay + y_end - 1));
18✔
902
  dst->has_changes = true;
18✔
903
}
904

905
//----------------------------------------------------------------------
906
void FVTerm::determineWindowLayers() noexcept
6✔
907
{
908
  // Determination of the window layer for all virtual windows
909

910
  const auto* vterm_win_list = getWindowList();
6✔
911

912
  if ( ! vterm_win_list || vterm_win_list->empty() )
6✔
913
    return;
×
914

915
  const auto& begin = vterm_win_list->cbegin();
6✔
916
  const auto& end = vterm_win_list->cend();
6✔
917
  auto iter = begin;
6✔
918

919
  while ( iter != end )
28✔
920
  {
921
    (*iter)->getPrintArea()->layer = int(std::distance(begin, iter) + 1);
22✔
922
    ++iter;
22✔
923
  }
924
}
925

926
//----------------------------------------------------------------------
927
void FVTerm::scrollAreaForward (FTermArea* area)
8✔
928
{
929
  // Scrolls the entire area on line up
930

931
  if ( ! area || area->size.height <= 1 )
8✔
932
    return;
×
933

934
  const int y_max = area->size.height - 1;
8✔
935
  const int x_max = area->size.width - 1;
8✔
936
  auto line_changes = area->changes_in_line.begin();
8✔
937
  const auto area_width = getFullAreaWidth(area);
8✔
938
  auto sc = area->data.cbegin() + area_width;  // source character
8✔
939
  auto dc = area->data.begin();  // destination character
8✔
940

941
  for (auto y{0}; y < y_max; y++)
97✔
942
  {
943
    putAreaLine (*sc, *dc, area->size.width);
89✔
944
    line_changes->xmin = 0;
89✔
945
    line_changes->xmax = uInt(x_max);
89✔
946
    ++line_changes;
89✔
947
    sc += area_width;
89✔
948
    dc += area_width;
89✔
949
  }
950

951
  // insert a new line below
952
  const auto& lc = area->getFChar(x_max, area->size.height - 2);  // last character
8✔
953
  nc.color.data = lc.color.data;
8✔
954
  nc.attr = lc.attr;
8✔
955
  nc.ch[0] = L' ';
8✔
956
  nc.ch[1] = L'\0';
8✔
957
  dc = area->data.begin() + (area_width * y_max);  // destination character
8✔
958
  std::fill (dc, dc + area->size.width, nc);
8✔
959
  auto new_line_changes = area->changes_in_line.begin() + y_max;
8✔
960
  new_line_changes->xmin = 0;
8✔
961
  new_line_changes->xmax = uInt(x_max);
8✔
962
  area->changes_in_row = {0, uInt(y_max)};
8✔
963
  area->has_changes = true;
8✔
964

965
  if ( area == vdesktop.get() )
8✔
966
    scrollTerminalForward();  // Scrolls the terminal up one line
3✔
967
}
968

969
//----------------------------------------------------------------------
970
void FVTerm::scrollAreaReverse (FTermArea* area)
12✔
971
{
972
  // Scrolls the entire area one line down
973

974
  if ( ! area || area->size.height <= 1 )
12✔
975
    return;
×
976

977
  const int y_max = area->size.height - 1;
12✔
978
  const int x_max = area->size.width - 1;
12✔
979
  auto line_changes = area->changes_in_line.begin() + y_max;
12✔
980
  const auto area_width = getFullAreaWidth(area);
12✔
981
  auto sc = area->data.cbegin() + (area_width * (y_max - 1));  // source character
12✔
982
  auto dc = area->data.begin() + (area_width * y_max);  // destination character
12✔
983

984
  for (auto y = y_max; y > 0; y--)
193✔
985
  {
986
    putAreaLine (*sc, *dc, area->size.width);
181✔
987
    line_changes->xmin = 0;
181✔
988
    line_changes->xmax = uInt(x_max);
181✔
989
    --line_changes;
181✔
990
    sc -= area_width;
181✔
991
    dc -= area_width;
181✔
992
  }
993

994
  // insert a new line above
995
  const auto& lc = area->getFChar(0, 1);  // last character
12✔
996
  nc.color.data = lc.color.data;
12✔
997
  nc.attr = lc.attr;
12✔
998
  nc.ch[0] = L' ';
12✔
999
  nc.ch[1] = L'\0';
12✔
1000
  dc = area->data.begin();  // destination character
12✔
1001
  std::fill (dc, dc + area->size.width, nc);
12✔
1002
  auto new_line_changes = area->changes_in_line.begin() + y_max;
12✔
1003
  new_line_changes->xmin = 0;
12✔
1004
  new_line_changes->xmax = uInt(x_max);
12✔
1005
  area->changes_in_row = {0, uInt(y_max)};
12✔
1006
  area->has_changes = true;
12✔
1007

1008
  if ( area == vdesktop.get() )
12✔
1009
    scrollTerminalReverse();  // Scrolls the terminal down one line
7✔
1010
}
1011

1012
//----------------------------------------------------------------------
1013
void FVTerm::clearArea (FTermArea* area, wchar_t fillchar) noexcept
3✔
1014
{
1015
  // Clear the area with the current attributes
1016

1017
  static const auto& next_attr = getAttribute();
3✔
1018
  nc.color.data = next_attr.color.data;
3✔
1019
  nc.attr = next_attr.attr;
3✔
1020
  nc.ch[0] = fillchar;  // Current attributes with the fill character
3✔
1021
  nc.ch[1] = L'\0';
3✔
1022
  nc.attr.bit.char_width = getColumnWidth(nc.ch[0]) & 0x03;
3✔
1023

1024
  if ( ! area || area->data.empty() )
3✔
1025
  {
1026
    foutput->clearTerminal (fillchar);
×
1027
    return;
2✔
1028
  }
1029

1030
  const auto width = uInt(getFullAreaWidth(area));
3✔
1031

1032
  if ( area->shadow.width == 0 )
3✔
1033
  {
1034
    if ( clearFullArea(area, nc) )
2✔
1035
      return;
2✔
1036
  }
1037
  else
1038
    clearAreaWithShadow(area, nc);
1✔
1039

1040
  auto line_changes = area->changes_in_line.begin();
1✔
1041

1042
  for (auto i{0}; i < area->size.height; i++)
21✔
1043
  {
1044
    line_changes->xmin = 0;
20✔
1045
    line_changes->xmax = width - 1;
20✔
1046

1047
    if ( nc.attr.bit.transparent
20✔
1048
      || nc.attr.bit.color_overlay
20✔
1049
      || nc.attr.bit.inherit_background )
20✔
1050
      line_changes->trans_count = width;
×
1051
    else if ( area->shadow.width != 0 )
20✔
1052
      line_changes->trans_count = uInt(area->shadow.width);
20✔
1053
    else
1054
      line_changes->trans_count = 0;
×
1055

1056
    ++line_changes;
20✔
1057
  }
1058

1059
  line_changes = area->changes_in_line.begin() + area->size.height;
1✔
1060

1061
  for (auto i{0}; i < area->shadow.height; i++)
2✔
1062
  {
1063
    line_changes->xmin = 0;
1✔
1064
    line_changes->xmax = width - 1;
1✔
1065
    line_changes->trans_count = width;
1✔
1066
    ++line_changes;
1✔
1067
  }
1068

1069
  area->changes_in_row = {0, uInt(getFullAreaHeight(area) - 1)};
1✔
1070
  area->has_changes = true;
1✔
1071
}
1072

1073
//----------------------------------------------------------------------
1074
void FVTerm::forceTerminalUpdate() const
44✔
1075
{
1076
  force_terminal_update = true;
44✔
1077
  processTerminalUpdate();
44✔
1078
  flush();
44✔
1079
  force_terminal_update = false;
44✔
1080
}
44✔
1081

1082
//----------------------------------------------------------------------
1083
auto FVTerm::processTerminalUpdate() const -> bool
70✔
1084
{
1085
  // Checks if the resizing of the terminal is not finished
1086

1087
  if ( foutput->hasTerminalResized() )
70✔
1088
    return false;
×
1089

1090
  // Update data on VTerm
1091
  if ( skip_one_vterm_update )
70✔
1092
    skip_one_vterm_update = false;
20✔
1093
  else
1094
    updateVTerm();
50✔
1095

1096
  // Update the visible terminal
1097
  return updateTerminal();
70✔
1098
}
1099

1100
//----------------------------------------------------------------------
1101
void FVTerm::startDrawing() noexcept
1✔
1102
{
1103
  // Pauses the terminal updates for the printing phase
1104
  draw_completed = false;
1✔
1105
}
1✔
1106

1107
//----------------------------------------------------------------------
1108
void FVTerm::finishDrawing() noexcept
2✔
1109
{
1110
  // After the printing phase is completed, the terminal will be updated
1111
  draw_completed = true;
2✔
1112
}
2✔
1113

1114
//----------------------------------------------------------------------
1115
void FVTerm::initTerminal()
2✔
1116
{
1117
  foutput->initTerminal(vterm.get());
2✔
1118
  tabstop = foutput->getTabstop();
2✔
1119
  resetAreaEncoding();
2✔
1120
}
2✔
1121

1122

1123
// private methods of FVTerm
1124
//----------------------------------------------------------------------
1125
void FVTerm::setGlobalFVTermInstance (FVTerm* ptr) noexcept
65✔
1126
{
1127
  getGlobalFVTermInstance() = ptr;
65✔
1128
}
65✔
1129

1130
//----------------------------------------------------------------------
1131
auto FVTerm::getGlobalFVTermInstance() noexcept -> FVTerm*&
214✔
1132
{
1133
  static FVTerm* init_object{nullptr};
1134
  return init_object;
214✔
1135
}
1136

1137
//----------------------------------------------------------------------
1138
auto FVTerm::isInitialized() noexcept -> bool
200✔
1139
{
1140
  return internal::var::fvterm_initialized;
200✔
1141
}
1142

1143
//----------------------------------------------------------------------
1144
void FVTerm::resetAreaEncoding() const
2✔
1145
{
1146
  auto encoding = foutput->getEncoding();
2✔
1147
  vdesktop->encoding = encoding;
2✔
1148
  vterm->encoding = encoding;
2✔
1149

1150
  for (auto&& vterm_obj : *vterm_window_list)
2✔
1151
  {
1152
    auto v_win = vterm_obj->getVWin();
×
1153

1154
    if ( ! v_win )
×
1155
      continue;
×
1156

1157
    v_win->encoding = encoding;
×
1158

1159
    for (const auto& pcall : v_win->preproc_list)
×
1160
    {
1161
      if ( pcall->instance && pcall->instance->child_print_area )
×
1162
        pcall->instance->child_print_area->encoding = encoding;
×
1163
    }
1164
  }
1165
}
2✔
1166

1167
//----------------------------------------------------------------------
1168
inline void FVTerm::resetTextAreaToDefault ( FTermArea* area
134✔
1169
                                           , const FSize& size ) const noexcept
1170
{
1171
  static constexpr FChar default_char
1172
  {
1173
    { L' ',  L'\0', L'\0', L'\0', L'\0' },
1174
    { L'\0', L'\0', L'\0', L'\0', L'\0' },
1175
    { { FColor::Default, FColor::Default } },
1176
    { { 0x00, 0x00, 0x08, 0x00} }  // byte 0..3 (byte 2 = 0x08 = char_width 1)
1177
  };
1178
  std::fill (area->data.begin(), area->data.end(), default_char);
134✔
1179

1180
  const FTermArea::FLineChanges unchanged { uInt(size.getWidth())
134✔
1181
                                          , 0, 0, false };
134✔
1182
  std::fill (area->changes_in_line.begin(), area->changes_in_line.end(), unchanged);
134✔
1183
  area->changes_in_row = { uInt(size.getHeight()), 0};
134✔
1184
}
134✔
1185

1186
//----------------------------------------------------------------------
1187
inline auto FVTerm::resizeTextArea ( FTermArea* area
131✔
1188
                                   , std::size_t height
1189
                                   , std::size_t size ) const -> bool
1190
{
1191
  // Set the number of lines for changes to "height"
1192
  // and resize the text area to "size" elements
1193

1194
  area->changes_in_line.resize(height);
131✔
1195
  resizeTextArea (area, size);
131✔
1196
  return true;
131✔
1197
}
1198

1199
//----------------------------------------------------------------------
1200
inline auto FVTerm::resizeTextArea (FTermArea* area, std::size_t size) const -> bool
134✔
1201
{
1202
  // Resize text area to "size" FChar elements
1203

1204
  area->data.resize(size);
134✔
1205
  return true;
134✔
1206
}
1207

1208
//----------------------------------------------------------------------
1209
auto FVTerm::isCovered (const FPoint& pos, const FTermArea* area) const noexcept -> CoveredState
3✔
1210
{
1211
  // Determines the covered state for the given position
1212

1213
  const auto* vterm_win_list{getWindowList()};
3✔
1214

1215
  if ( ! area || ! vterm_win_list || vterm_win_list->empty()
3✔
1216
    || vterm_win_list->back()->getVWin() == area )  // Top window can't be covered
6✔
1217
    return CoveredState::None;
3✔
1218

1219
  const auto pos_x = pos.getX();
×
1220
  const auto pos_y = pos.getY();
×
1221

1222
  auto is_covered{CoveredState::None};  // Initial state: no coverage
×
1223
  bool found{ area == vdesktop.get() };
×
1224

1225
  for (const auto& vterm_obj : *vterm_win_list)  // Iteration through window list
×
1226
  {
1227
    const auto* win = vterm_obj->getVWin();
×
1228

1229
    if ( ! win || ! win->visible )
×
1230
      continue;
×
1231

1232
    if ( ! found )  // Check if we've found the target area yet
×
1233
    {
1234
      found = ( area == win );
×
1235
      continue;
×
1236
    }
1237

1238
    const int win_x_min = win->position.x;
×
1239
    const int win_y_min = win->position.y;
×
1240

1241
    if ( pos_x < win_x_min || pos_y < win_y_min )
×
1242
      continue;
×
1243

1244
    const int current_height = win->minimized ? win->min_size.height
×
1245
                                              : getFullAreaHeight(win);
×
1246
    const int win_x_max = win_x_min + getFullAreaWidth(win);
×
1247
    const int win_y_max = win_y_min + current_height;
×
1248

1249
    if ( pos_x >= win_x_max || pos_y >= win_y_max )
×
1250
      continue;
×
1251

1252
    // Position is covered by this window - check for transparency
1253
    const auto delta_x = pos_x - win->position.x;
×
1254
    const auto delta_y = pos_y - win->position.y;
×
1255
    const auto width = getFullAreaWidth(win);
×
1256
    const auto index = unsigned(delta_y) * unsigned(width) + unsigned(delta_x);
×
1257
    const auto& character = win->data[index];
×
1258

1259
    if ( character.attr.bit.transparent )
×
1260
      continue;
×
1261

1262
    if ( character.attr.bit.color_overlay )  // Color overlay = half covered
×
1263
      is_covered = CoveredState::Half;  // Mark as partially covered
×
1264
    else
1265
      return CoveredState::Full;  // Fully covered
×
1266
  }
1267

1268
  return is_covered;
×
1269
}
1270

1271
//----------------------------------------------------------------------
1272
inline auto FVTerm::isAreaValid (const FShadowBox& shadowbox) const noexcept -> bool
139✔
1273
{
1274
  return shadowbox.box.getY() >= 0
139✔
1275
      && shadowbox.box.getWidth() > 0
139✔
1276
      && shadowbox.box.getHeight() > 0;
278✔
1277
}
1278

1279
//----------------------------------------------------------------------
1280
inline auto FVTerm::isSizeEqual (const FTermArea* area, const FShadowBox& shadowbox) const noexcept -> bool
139✔
1281
{
1282
  return area->size.width == int(shadowbox.box.getWidth())
139✔
1283
      && area->size.height == int(shadowbox.box.getHeight())
5✔
1284
      && area->shadow.width == int(shadowbox.shadow.getWidth())
5✔
1285
      && area->shadow.height == int(shadowbox.shadow.getHeight());
144✔
1286
}
1287

1288
//----------------------------------------------------------------------
1289
constexpr auto FVTerm::needsHeightResize ( const FTermArea* area
134✔
1290
                                         , const std::size_t full_height ) const noexcept -> bool
1291
{
1292
  return getFullAreaHeight(area) != int(full_height);
134✔
1293
}
1294

1295
//----------------------------------------------------------------------
1296
constexpr auto FVTerm::needsWidthResize ( const FTermArea* area
3✔
1297
                                        , const std::size_t full_width ) const noexcept -> bool
1298
{
1299
  return getFullAreaWidth(area) != int(full_width);
3✔
1300
}
1301

1302
//----------------------------------------------------------------------
1303
inline auto FVTerm::tryResizeArea ( FTermArea* area
134✔
1304
                                  , const std::size_t width
1305
                                  , const std::size_t height ) const -> bool
1306
{
1307
  if ( needsHeightResize(area, height) )
134✔
1308
    return resizeTextArea(area, height, width * height);
131✔
1309

1310
  if ( needsWidthResize(area, width) )
3✔
1311
    return resizeTextArea(area, width * height);
3✔
1312

1313
  return false;
×
1314
}
1315

1316
//----------------------------------------------------------------------
1317
inline void FVTerm::updateAreaProperties (FTermArea* area, const FShadowBox& shadowbox) const noexcept
134✔
1318
{
1319
  area->position.x      = shadowbox.box.getX();
134✔
1320
  area->position.y      = shadowbox.box.getY();
134✔
1321
  area->size.width      = int(shadowbox.box.getWidth());
134✔
1322
  area->size.height     = int(shadowbox.box.getHeight());
134✔
1323
  area->min_size.width  = area->size.width;
134✔
1324
  area->min_size.height = DEFAULT_MINIMIZED_HEIGHT;
134✔
1325
  area->shadow.width    = int(shadowbox.shadow.getWidth());
134✔
1326
  area->shadow.height   = int(shadowbox.shadow.getHeight());
134✔
1327
  area->has_changes     = false;
134✔
1328
}
134✔
1329

1330
//----------------------------------------------------------------------
1331
constexpr auto FVTerm::getFullAreaWidth (const FTermArea* area) const noexcept -> int
3,865✔
1332
{
1333
  return area->size.width + area->shadow.width;
3,865✔
1334
}
1335

1336
//----------------------------------------------------------------------
1337
constexpr auto FVTerm::getFullAreaHeight (const FTermArea* area) const noexcept -> int
3,786✔
1338
{
1339
  return area->size.height + area->shadow.height;
3,786✔
1340
}
1341

1342
//----------------------------------------------------------------------
1343
void FVTerm::passChangesToOverlap (const FTermArea* area) const
34✔
1344
{
1345
  const auto* vterm_win_list = getWindowList();
34✔
1346

1347
  if ( ! area || ! vterm_win_list || vterm_win_list->empty() )
34✔
1348
    return;
×
1349

1350
  processOverlappingWindows (area, *vterm_win_list);
34✔
1351
}
1352

1353
//----------------------------------------------------------------------
1354
inline void FVTerm::processOverlappingWindows ( const FTermArea* area
34✔
1355
                                              , const FVTermList& vterm_win_list ) const
1356
{
1357
  bool found{false};
34✔
1358

1359
  if ( ! area )
34✔
1360
    return;
×
1361

1362
  for (const auto& vterm_obj : vterm_win_list)
172✔
1363
  {
1364
    auto* win = vterm_obj->getVWin();
138✔
1365

1366
    if ( found && win && win->visible && win->isOverlapped(area) )
138✔
1367
    {
1368
      // Pass changes to the found window
1369
      passChangesToOverlappingWindow (win, area);
32✔
1370
    }
1371

1372
    if ( area == win )
138✔
1373
      found = true;
34✔
1374
  }
1375
}
1376

1377
//----------------------------------------------------------------------
1378
inline void FVTerm::passChangesToOverlappingWindow (FTermArea* win, const FTermArea* area) const noexcept
32✔
1379
{
1380
  win->has_changes = true;
32✔
1381

1382
  // Boundaries
1383
  const int win_y_min = win->position.y;
32✔
1384
  const int win_height = win->minimized ? win->min_size.height : getFullAreaHeight(win);
32✔
1385
  const int win_y_max = win_y_min + win_height - 1;
32✔
1386
  const int area_y_min = area->position.y;
32✔
1387
  const int area_height = area->minimized ? area->min_size.height : getFullAreaHeight(area);
32✔
1388
  const int area_y_max = area_y_min + area_height - 1;
32✔
1389
  const int vterm_y_max = vterm->size.height - 1;
32✔
1390

1391
  // Calculate start and end line
1392
  const int y_start = calculateStartCoordinate (area_y_min, win_y_min);
32✔
1393
  const int y_end = calculateEndCoordinate (vterm_y_max, area_y_max, win_y_min, win_y_max);
32✔
1394

1395
  for (auto y{y_start}; y <= y_end; y++)  // Line loop
82✔
1396
  {
1397
    passChangesToOverlappingWindowLine (win, y, area);
50✔
1398
  }
1399

1400
  win->changes_in_row = {uInt(y_start), uInt(y_end)};
32✔
1401
}
32✔
1402

1403
//----------------------------------------------------------------------
1404
inline void FVTerm::passChangesToOverlappingWindowLine (FTermArea* win, int y, const FTermArea* area) const noexcept
50✔
1405
{
1406
  // Boundaries
1407
  const int win_x_min = win->position.x;
50✔
1408
  const int win_x_max = win_x_min + getFullAreaWidth(win) - 1;
50✔
1409
  const int area_x_min = area->position.x;
50✔
1410
  const int area_x_max = area_x_min + getFullAreaWidth(area) - 1;
50✔
1411
  const int vterm_x_max = vterm->size.width - 1;
50✔
1412

1413
  // Calculate start and end position
1414
  const int x_start = calculateStartCoordinate (area_x_min, win_x_min);
50✔
1415
  const int x_end = calculateEndCoordinate (vterm_x_max, area_x_max, win_x_min, win_x_max);
50✔
1416

1417
  // Sets the new change boundaries
1418
  auto& line_changes = win->changes_in_line[unsigned(y)];
50✔
1419
  line_changes.xmin = uInt(std::min(int(line_changes.xmin), x_start));
50✔
1420
  line_changes.xmax = uInt(std::max(int(line_changes.xmax), x_end));
50✔
1421
}
50✔
1422

1423
//----------------------------------------------------------------------
1424
inline auto FVTerm::calculateStartCoordinate (int area_min, int win_min) const noexcept -> int
82✔
1425
{
1426
  return std::max({0, area_min, win_min}) - win_min;
82✔
1427
}
1428

1429
//----------------------------------------------------------------------
1430
inline auto FVTerm::calculateEndCoordinate (int vterm_max, int area_max, int win_min, int win_max) const noexcept -> int
82✔
1431
{
1432
  return std::min({vterm_max, area_max, win_max}) - win_min;
82✔
1433
}
1434

1435
//----------------------------------------------------------------------
1436
void FVTerm::updateVTerm() const
50✔
1437
{
1438
  // Updates the character data from all areas to VTerm
1439

1440
  updateVTermDesktop();
50✔
1441

1442
  if ( ! vterm_window_list || vterm_window_list->empty() )
50✔
1443
    return;
32✔
1444

1445
  for (const auto& vterm_obj : *vterm_window_list)  // List from bottom to top
84✔
1446
  {
1447
    auto v_win = vterm_obj->getVWin();
66✔
1448

1449
    if ( ! (v_win && v_win->visible && v_win->layer > 0) )
66✔
1450
      continue;
19✔
1451

1452
    updateVTermWindow(v_win);
47✔
1453
  }
1454
}
1455

1456
//----------------------------------------------------------------------
1457
inline void FVTerm::updateVTermDesktop() const
50✔
1458
{
1459
  if ( ! hasPendingUpdates(vdesktop.get()) )
50✔
1460
    return;
43✔
1461

1462
  addLayer(vdesktop.get());  // Add vdesktop changes to vterm
7✔
1463
  vdesktop->has_changes = false;
7✔
1464
}
1465

1466
//----------------------------------------------------------------------
1467
inline void FVTerm::updateVTermWindow (FTermArea* v_win) const
47✔
1468
{
1469
  if ( hasPendingUpdates(v_win) )
47✔
1470
  {
1471
    passChangesToOverlap(v_win);
34✔
1472
    addLayer(v_win);  // Add v_win changes to vterm
34✔
1473
    v_win->has_changes = false;
34✔
1474
  }
1475
  else if ( hasChildAreaChanges(v_win) )
13✔
1476
  {
1477
    passChangesToOverlap(v_win);
×
1478
    addLayer(v_win);  // and call the child area processing handler there
×
1479
    clearChildAreaChanges(v_win);
×
1480
  }
1481
}
47✔
1482
//----------------------------------------------------------------------
1483
inline auto FVTerm::computeLayerGeometry (const FTermArea* area) const noexcept -> LayerGeometry
88✔
1484
{
1485
  // Compute the geometry values used throughout the rendering process
1486

1487
  const auto area_x = area->position.x;
88✔
1488
  const auto area_y = area->position.y;
88✔
1489
  const auto vterm_width = vterm->size.width;
88✔
1490
  const auto vterm_height = vterm->size.height;
88✔
1491
  const auto height = area->minimized ? area->min_size.height : getFullAreaHeight(area);
88✔
1492

1493
  return LayerGeometry
1494
  {
1495
    area_x,
1496
    area_y,
1497
    std::max(0, -area_x),
264✔
1498
    vterm_width,
1499
    vterm_height,
1500
    std::max(area_x, 0),
264✔
1501
    getFullAreaWidth(area),
88✔
1502
    height,
1503
    vterm_width - area_x - 1,
88✔
1504
    // Calculate actual rendering range (clipped to vterm)
1505
    std::max(0, -area_y),
264✔
1506
    std::min(vterm_height - area_y, height)
176✔
1507
  };
88✔
1508
}
1509

1510
//----------------------------------------------------------------------
1511
inline auto FVTerm::isLayerOutsideVTerm (const LayerGeometry& geo) const noexcept -> bool
88✔
1512
{
1513
  if ( geo.area_y >= static_cast<int>(geo.vterm_height) )
88✔
1514
    return false;
×
1515

1516
  if ( geo.ax >= geo.vterm_width )
88✔
1517
    return false;
×
1518

1519
  if ( geo.area_y + geo.height <= 0 )
88✔
1520
    return false;
×
1521

1522
  return true;
88✔
1523
}
1524

1525
//----------------------------------------------------------------------
1526
inline void FVTerm::buildLineChangeBatch ( const FTermArea* area
88✔
1527
                                         , const LayerGeometry& geo ) const noexcept
1528
{
1529
  int prev_xmin{-1};
88✔
1530
  int prev_xmax{-1};
88✔
1531
  NoTrans prev_has_no_trans{NoTrans::Undefined};
88✔
1532

1533
  auto line_changes = area->changes_in_line.begin() + geo.y_start;
88✔
1534

1535
  for (auto y{geo.y_start}; y < geo.y_end; y++)  // Line loop
943✔
1536
  {
1537
    const auto line_xmin_raw = int(line_changes->xmin);
855✔
1538
    const auto line_xmax_raw = int(line_changes->xmax);
855✔
1539

1540
    if ( line_xmin_raw > line_xmax_raw )
855✔
1541
    {
1542
      ++line_changes;
364✔
1543
      prev_xmin = -1;
364✔
1544
      prev_xmax = -1;
364✔
1545
      prev_has_no_trans = NoTrans::Undefined;
364✔
1546
      continue;
754✔
1547
    }
1548

1549
    const auto line_xmin = std::max(line_xmin_raw, geo.ol);
491✔
1550
    const auto line_xmax = std::min(line_xmax_raw, geo.xmax_inside_vterm);
491✔
1551

1552
    if ( line_xmin > line_xmax )
491✔
1553
    {
1554
      ++line_changes;
×
1555
      prev_xmin = -1;
×
1556
      prev_xmax = -1;
×
1557
      prev_has_no_trans = NoTrans::Undefined;
×
1558
      continue;
×
1559
    }
1560

1561
    const NoTrans has_no_trans = line_changes->trans_count == 0 ? NoTrans::Set : NoTrans::Unset;
491✔
1562
    ++line_changes;
491✔
1563

1564
    if ( prev_xmin == line_xmin
491✔
1565
      && prev_xmax == line_xmax
410✔
1566
      && prev_has_no_trans == has_no_trans )
408✔
1567
    {
1568
      line_changes_batch.back().count++;
390✔
1569
      continue;
390✔
1570
    }
1571

1572
    line_changes_batch.push_back({1, y, line_xmin, line_xmax, has_no_trans});
101✔
1573
    prev_xmin = line_xmin;
101✔
1574
    prev_xmax = line_xmax;
101✔
1575
    prev_has_no_trans = has_no_trans;
101✔
1576
  }
1577
}
88✔
1578

1579
//----------------------------------------------------------------------
1580
inline void FVTerm::applyLineBatch ( FTermArea* area
72✔
1581
                                   , const LayerGeometry& geo ) const noexcept
1582
{
1583
  for (const auto& line : line_changes_batch)
173✔
1584
  {
1585
    const auto line_xmin = line.xmin;
101✔
1586
    const auto line_xmax = line.xmax;
101✔
1587
    const auto has_no_trans = line.has_no_transparency == NoTrans::Set;
101✔
1588
    const auto tx = geo.area_x + line_xmin;  // Global terminal x-position
101✔
1589
    const int length = line_xmax - line_xmin + 1;
101✔
1590

1591
    // Process all lines in batch with same operation
1592
    for (int i = 0; i < line.count; ++i)
592✔
1593
    {
1594
      const auto y = line.ypos + i;
491✔
1595
      auto line_changes = area->changes_in_line.begin() + y;
491✔
1596
      const auto ty = geo.area_y + y;  // Global terminal y-position
491✔
1597
      const auto area_line_offset = y * geo.width + line_xmin;
491✔
1598
      const auto vterm_line_offset = ty * geo.vterm_width + tx;
491✔
1599
      auto ac = area->data.begin() + area_line_offset;    // Area character
491✔
1600
      auto tc = vterm->data.begin() + vterm_line_offset;  // Terminal character
491✔
1601

1602
      if ( has_no_trans )
491✔
1603
      {
1604
        // Line has only covered characters
1605
        putAreaLine (*ac, *tc, length);
317✔
1606
      }
1607
      else
1608
      {
1609
        // Line with hidden and transparent characters
1610
        addAreaLineWithTransparency (ac, tc, length);
174✔
1611
      }
1612

1613
      auto vterm_changes = vterm->changes_in_line.begin() + ty;
491✔
1614
      const auto tx_start = uInt(tx);
491✔
1615
      const auto tx_end   = uInt(std::min( geo.ax + line_xmax
982✔
1616
                                         , geo.vterm_width - 1 ));
491✔
1617
      vterm_changes->xmin = std::min(vterm_changes->xmin, tx_start);
491✔
1618
      vterm_changes->xmax = std::max(vterm_changes->xmax, tx_end);
491✔
1619

1620
      line_changes->xmin = uInt(geo.width);
491✔
1621
      line_changes->xmax = 0;
491✔
1622
      ++line_changes;
491✔
1623
    }
1624
  }
1625
}
72✔
1626

1627
//----------------------------------------------------------------------
1628
inline void FVTerm::updateVTermChangesFromBatch (const LayerGeometry& geo) const noexcept
72✔
1629
{
1630
  const auto& first = line_changes_batch.front();
72✔
1631
  const auto& last  = line_changes_batch.back();
72✔
1632
  const auto begin = uInt(geo.area_y + first.ypos - geo.y_start);
72✔
1633
  const auto end = uInt(geo.area_y + last.ypos + last.count - 1 - geo.y_start);
72✔
1634

1635
  auto& changes_in_row = vterm->changes_in_row;
72✔
1636
  changes_in_row.ymin = std::min(changes_in_row.ymin, begin);
72✔
1637
  changes_in_row.ymax = std::max(changes_in_row.ymax, end);
72✔
1638
  vterm->has_changes = true;
72✔
1639
}
72✔
1640

1641
//----------------------------------------------------------------------
1642
inline void FVTerm::scrollTerminalForward() const
3✔
1643
{
1644
  // Scrolls the terminal up one line
1645

1646
  foutput->flush();  // Empty buffer before scrolling
3✔
1647

1648
  if ( ! foutput->scrollTerminalForward() )
3✔
1649
    return;
×
1650

1651
  // Avoid update lines from 0 to (height - 2)
1652
  auto vdesktop_changes = vdesktop->changes_in_line.begin();
3✔
1653
  const auto vdesktop_changes_end = vdesktop_changes
1654
                                  + vdesktop->size.height - 1;
3✔
1655

1656
  while ( vdesktop_changes < vdesktop_changes_end )
72✔
1657
  {
1658
    vdesktop_changes->xmin = uInt(vdesktop->size.width - 1);
69✔
1659
    vdesktop_changes->xmax = 0;
69✔
1660
    ++vdesktop_changes;
69✔
1661
  }
1662

1663
  vdesktop->changes_in_row = {0, uInt(vdesktop->size.height - 2)};
3✔
1664
  putArea (FPoint{1, 1}, vdesktop.get());
3✔
1665
  saveCurrentVTerm();  // Ensure that the current terminal is comparable
3✔
1666
  forceTerminalUpdate();
3✔
1667
}
1668

1669
//----------------------------------------------------------------------
1670
inline void FVTerm::scrollTerminalReverse() const
7✔
1671
{
1672
  // Scrolls the terminal down one line
1673

1674
  foutput->flush();  // Empty buffer before scrolling
7✔
1675

1676
  if ( ! foutput->scrollTerminalReverse() )
7✔
1677
    return;
×
1678

1679
  // avoid update lines from 1 to (height - 1)
1680
  auto vdesktop_changes = vdesktop->changes_in_line.begin() + 1;
7✔
1681
  const auto vdesktop_changes_end = vdesktop_changes
1682
                                  + vdesktop->size.height;
7✔
1683

1684
  while ( vdesktop_changes < vdesktop_changes_end )
175✔
1685
  {
1686
    vdesktop_changes->xmin = uInt(vdesktop->size.width - 1);
168✔
1687
    vdesktop_changes->xmax = 0;
168✔
1688
    ++vdesktop_changes;
168✔
1689
  }
1690

1691
  vdesktop->changes_in_row = {1, uInt(vdesktop->size.height - 1)};
7✔
1692
  putArea (FPoint{1, 1}, vdesktop.get());
7✔
1693
  saveCurrentVTerm();  // Ensure that the current terminal is comparable
7✔
1694
  forceTerminalUpdate();
7✔
1695
}
1696

1697
//----------------------------------------------------------------------
1698
void FVTerm::callPreprocessingHandler (const FTermArea* area) const
88✔
1699
{
1700
  // Call preprocessing handler
1701

1702
  if ( ! area || area->preproc_list.empty() )
88✔
1703
    return;
84✔
1704

1705
  for (const auto& pcall : area->preproc_list)
8✔
1706
  {
1707
    // call the preprocessing handler
1708
    const auto& preprocessingHandler = pcall->function;
4✔
1709
    preprocessingHandler();
4✔
1710
  }
1711
}
1712

1713
//----------------------------------------------------------------------
1714
auto FVTerm::hasChildAreaChanges (const FTermArea* area) const -> bool
13✔
1715
{
1716
  if ( ! area || area->preproc_list.empty() )
13✔
1717
    return false;
13✔
1718

1719
  return std::any_of ( area->preproc_list.cbegin()
×
1720
                     , area->preproc_list.cend()
1721
                     , [] (const auto& pcall) noexcept
×
1722
                       {
1723
                         return pcall->instance
×
1724
                             && pcall->instance->child_print_area
×
1725
                             && pcall->instance->child_print_area->has_changes;
×
1726
                       }
1727
                     );
×
1728
}
1729

1730
//----------------------------------------------------------------------
1731
void FVTerm::clearChildAreaChanges (const FTermArea* area) const
×
1732
{
1733
  if ( ! area || area->preproc_list.empty() )
×
1734
    return;
×
1735

1736
  for (const auto& pcall : area->preproc_list)
×
1737
  {
1738
    if ( pcall->instance && pcall->instance->child_print_area )
×
1739
      pcall->instance->child_print_area->has_changes = false;
×
1740
  }
1741
}
1742

1743
//----------------------------------------------------------------------
1744
constexpr auto FVTerm::isInsideArea (const FPoint& pos, const FTermArea* area) noexcept -> bool
4✔
1745
{
1746
  // Check whether the coordinates are within the area
1747

1748
  const int x = pos.getX();
4✔
1749
  const int y = pos.getY();
4✔
1750
  return x >= 0 && x < area->size.width
4✔
1751
      && y >= 0 && y < area->size.height;
8✔
1752
}
1753

1754
//----------------------------------------------------------------------
1755
inline auto FVTerm::isInsideTerminal (const FPoint& pos) const noexcept -> bool
4✔
1756
{
1757
  // Check whether the coordinates are within the virtual terminal
1758

1759
  const auto x = pos.getX();
4✔
1760
  const auto y = pos.getY();
4✔
1761
  return x >= 0 && x < int(foutput->getColumnNumber())
3✔
1762
      && y >= 0 && y < int(foutput->getLineNumber());
7✔
1763
}
1764

1765
//----------------------------------------------------------------------
1766
constexpr auto FVTerm::isFCharTransparent (const FChar& fchar) noexcept -> bool
2,321✔
1767
{
1768
  return (fchar.attr.data & internal::var::transparent_mask) != 0;
2,321✔
1769
}
1770

1771
//----------------------------------------------------------------------
1772
inline auto FVTerm::isTransparentInvisible (const FChar& fchar) const noexcept -> bool
134✔
1773
{
1774
  static const auto& trans_inv_chars = getTransparentInvisibleLookupMap();
134✔
1775
  const auto& fist_char = fchar.ch.unicode_data[0];
134✔
1776
  return trans_inv_chars.find(fist_char) != trans_inv_chars.end();
134✔
1777
}
1778

1779
//----------------------------------------------------------------------
1780
void FVTerm::initSettings()
33✔
1781
{
1782
  // Presetting of the current locale for full-width character support.
1783
  // The final setting is made later in FTerm::init_locale().
1784
  std::setlocale (LC_ALL, "");
33✔
1785

1786
  // Initialize the printing attribute
1787
  FVTermAttribute::initAttribute();
33✔
1788

1789
  // Create virtual terminal
1790
  FSize term_size {foutput->getColumnNumber(), foutput->getLineNumber()};
33✔
1791
  createVTerm (term_size);
33✔
1792

1793
  // Create virtual desktop area
1794
  createVDesktop (term_size);
33✔
1795
  active_area = vdesktop.get();
33✔
1796

1797
  // Reserving a typical number of changes
1798
  line_changes_batch.reserve(32);
33✔
1799

1800
  // Reservation of capacity for at least 16 areas
1801
  covered_areas_buffer.reserve(16);
33✔
1802

1803
  // fvterm is now initialized
1804
  internal::var::fvterm_initialized = true;
33✔
1805
}
33✔
1806

1807
//----------------------------------------------------------------------
1808
void FVTerm::finish() const
32✔
1809
{
1810
  // Reset the FINAL CUT terminal settings and
1811
  // return to the normal terminal mode
1812
  setNormal();
32✔
1813
  foutput->finishTerminal();
32✔
1814
  forceTerminalUpdate();
32✔
1815
  internal::var::fvterm_initialized = false;
32✔
1816
  setGlobalFVTermInstance(nullptr);
32✔
1817
}
32✔
1818

1819
//----------------------------------------------------------------------
1820
inline void FVTerm::saveCurrentVTerm() const noexcept
13✔
1821
{
1822
  // Save the content of the virtual terminal
1823
  std::memcpy(vterm_old->data.data(), vterm->data.data(), vterm->data.size() * sizeof(FChar));
13✔
1824
}
13✔
1825

1826
//----------------------------------------------------------------------
1827
inline void FVTerm::putAreaLine ( const FChar& src_char
1,091✔
1828
                                , FChar& dst_char
1829
                                , const int length ) const noexcept
1830
{
1831
  // copy "length" characters from area to terminal
1832

1833
  std::memcpy (&dst_char, &src_char, std::size_t(length) * sizeof(dst_char));
1,091✔
1834
}
1,091✔
1835

1836
//----------------------------------------------------------------------
1837
inline void FVTerm::applyColorOverlay (const FChar& src_char, FChar& dst_char) const
×
1838
{
1839
  dst_char.ch = src_char.ch;  // Get covered character
×
1840
  dst_char.attr.data &= ~internal::var::color_overlay_mask;
×
1841

1842
  if ( isTransparentInvisible(dst_char) )
×
1843
    dst_char.ch[0] = L' ';
×
1844
}
×
1845

1846
//----------------------------------------------------------------------
1847
inline void FVTerm::inheritBackground (const FChar& src_char, FChar& dst_char) const
×
1848
{
1849
  // Add the covered background to this character
1850
  dst_char.color.pair.bg = src_char.color.pair.bg;
×
1851
}
×
1852

1853
//----------------------------------------------------------------------
1854
inline void FVTerm::putMultiLayerAreaLine ( FChar_iterator dst_char
44✔
1855
                                          , const int length
1856
                                          , const FPoint& pos ) const noexcept
1857
{
1858
  if ( length == 0 )
44✔
1859
    return;
×
1860

1861
  if ( overlay_search_buffer.size() < std::size_t(length) )  // Resize only if needed
44✔
1862
    overlay_search_buffer.resize(std::size_t(length));
2✔
1863

1864
  auto search_buffer = overlay_search_buffer.begin();
44✔
1865
  std::fill_n(search_buffer, length, SearchState::start);
44✔
1866
  const auto term_x = pos.getX();
44✔
1867
  const auto term_y = pos.getY();
44✔
1868

1869
  overlay_line_buffer.clear();  // Clear buffer
44✔
1870

1871
  for (const auto* win : covered_areas_buffer)
132✔
1872
  {
1873
    // Precalculate window boundaries
1874
    const int current_height = win->minimized ? win->min_size.height
88✔
1875
                                              : getFullAreaHeight(win);
88✔
1876
    const int x_min = win->position.x;
88✔
1877
    const int x_max = x_min + getFullAreaWidth(win);
88✔
1878
    const int y_min = win->position.y;
88✔
1879
    const int y_max = y_min + current_height;
88✔
1880

1881
    // Early rejection if the line is completely outside the window
1882
    if ( term_y < y_min
88✔
1883
      || term_y >= y_max
88✔
1884
      || term_x >= x_max
88✔
1885
      || term_x + length <= x_min )
88✔
1886
      continue;
×
1887

1888
    // Precalculate array indexing values for getFChar
1889
    const auto x = term_x - x_min;
88✔
1890
    const auto y = term_y - y_min;
88✔
1891
    const auto width = getFullAreaWidth(win);
88✔
1892
    const auto index = y * width + x;
88✔
1893

1894
    // Calculate the intersection of the line with the window
1895
    const auto start_idx = std::max(0, x_min - term_x);
88✔
1896
    const auto end_idx = std::min(length, x_max - term_x);
88✔
1897

1898
    // Store pre-calculated area line data
1899
    overlay_line_buffer.push_back
1900
    (
88✔
1901
      { win->data.begin(), index, start_idx, end_idx }
88✔
1902
    );
1903
  }
1904

1905
  for (const auto& line : overlay_line_buffer)
132✔
1906
  {
1907
    auto offset = line.start_idx + line.offset;
88✔
1908
    auto win_char = line.iter + offset;
88✔
1909
    auto dst = dst_char + line.start_idx;
88✔
1910

1911
    // Process only the intersecting part of the line
1912
    for (std::ptrdiff_t idx{line.start_idx}; idx < line.end_idx; ++idx, ++win_char, ++dst)
340✔
1913
    {
1914
      auto& char_search = *(search_buffer + idx);
252✔
1915

1916
      if ( char_search == SearchState::ready )
252✔
1917
        continue;
×
1918

1919
      const auto transparency = win_char->attr.data
252✔
1920
                              & internal::var::transparent_mask;
252✔
1921

1922
      if ( transparency  == internal::var::transparent )  // Transparent
252✔
1923
      {
1924
        if ( char_search == SearchState::start )
126✔
1925
          char_search = SearchState::printable;
126✔
1926

1927
        continue;
126✔
1928
      }
1929

1930
      if ( transparency == internal::var::color_overlay )  // Color overlay
126✔
1931
      {
1932
        if ( char_search == SearchState::background )
×
1933
        {
1934
          inheritBackground(*win_char, *dst);
×
1935
          char_search = SearchState::ready;
×
1936
        }
1937
        else if ( char_search != SearchState::overlay )
×
1938
        {
NEW
1939
          std::memcpy (&dst[0], &win_char[0], sizeof(FChar));
×
1940
          char_search = SearchState::overlay;
×
1941
        }
1942

1943
        continue;
×
1944
      }
1945

1946
      if ( transparency == internal::var::inherit_background )  // Inherit background color
126✔
1947
      {
1948
        if ( char_search == SearchState::overlay )
×
1949
        {
1950
          applyColorOverlay(*win_char, *dst);
×
1951
          char_search = SearchState::ready;
×
1952
        }
1953
        else if ( char_search != SearchState::background )
×
1954
        {
NEW
1955
          std::memcpy (&dst[0], &win_char[0], sizeof(FChar));
×
1956
          char_search = SearchState::background;
×
1957
        }
1958

1959
        continue;
×
1960
      }
1961

1962
      // transparency = 0
1963
      if ( char_search == SearchState::overlay )  // Color overlay
126✔
1964
      {
1965
        applyColorOverlay(*win_char, *dst);
×
1966
      }
1967
      else if ( char_search == SearchState::background )  // Inherit background color
126✔
1968
      {
1969
        inheritBackground(*win_char, *dst);
×
1970
      }
1971
      else  // Non-transparent
1972
      {
1973
        std::memcpy (&dst[0], &win_char[0], sizeof(FChar));
126✔
1974
      }
1975

1976
      char_search = SearchState::ready;
126✔
1977
    }
1978
  }
1979
}
1980

1981
//----------------------------------------------------------------------
1982
inline void FVTerm::putAreaLineWithTransparency ( FChar_const_iterator src_char
64✔
1983
                                                , FChar_iterator dst_char
1984
                                                , const int length
1985
                                                , FPoint pos
1986
                                                , bool line_covered) const
1987
{
1988
  if ( length < 1 )
64✔
1989
    return;
×
1990

1991
  bool is_region_transparent = isFCharTransparent(*src_char);
64✔
1992
  int region_count{1};
64✔
1993
  const auto end_char = src_char + length;
64✔
1994
  auto region_start = src_char;
64✔
1995
  auto current_char = src_char + 1;
64✔
1996
  const auto unroll_end = end_char - 3;
64✔
1997

1998
  while ( current_char < unroll_end )  // Main unrolled loop
253✔
1999
  {
2000
    const std::array<bool, 4> trans_data
2001
    {{
2002
      isFCharTransparent(*current_char),
189✔
2003
      isFCharTransparent(*(current_char + 1)),
189✔
2004
      isFCharTransparent(*(current_char + 2)),
189✔
2005
      isFCharTransparent(*(current_char + 3))
189✔
2006
    }};
567✔
2007

2008
    if ( trans_data[0] == is_region_transparent
189✔
2009
      && trans_data[1] == is_region_transparent
189✔
2010
      && trans_data[2] == is_region_transparent
189✔
2011
      && trans_data[3] == is_region_transparent )
378✔
2012
    {
2013
      region_count += 4;
169✔
2014
      current_char += 4;
169✔
2015
      continue;
169✔
2016
    }
2017

2018
    for (uInt i = 0; i < 4; ++i)
100✔
2019
    {
2020
      const bool is_current_char_transparent = trans_data[i];
80✔
2021

2022
      if ( is_current_char_transparent == is_region_transparent )
80✔
2023
      {
2024
        ++region_count;
60✔
2025
      }
2026
      else
2027
      {
2028
        // Process completed region
2029
        if ( is_region_transparent || line_covered )
20✔
2030
          putMultiLayerAreaLine (dst_char, region_count, pos);
×
2031
        else
2032
          putAreaLine (*region_start, *dst_char, region_count);
20✔
2033

2034
        pos.x_ref() += region_count;
20✔
2035
        dst_char += region_count;
20✔
2036

2037
        // Reset for the next region
2038
        is_region_transparent = is_current_char_transparent;
20✔
2039
        region_start = current_char;
20✔
2040
        region_count = 1;
20✔
2041
      }
2042

2043
      ++current_char;
80✔
2044
    }
2045
  }
2046

2047
  while ( current_char < end_char )  // Process remaining characters (tail)
170✔
2048
  {
2049
    const bool is_current_transparent = isFCharTransparent(*current_char);
106✔
2050

2051
    if ( is_current_transparent == is_region_transparent )
106✔
2052
    {
2053
      ++region_count;
86✔
2054
    }
2055
    else
2056
    {
2057
      // Process completed region
2058
      if ( is_region_transparent || line_covered )
20✔
2059
        putMultiLayerAreaLine (dst_char, region_count, pos);
×
2060
      else
2061
        putAreaLine (*region_start, *dst_char, region_count);
20✔
2062

2063
      pos.x_ref() += region_count;
20✔
2064
      dst_char += region_count;
20✔
2065

2066
      // Start new region
2067
      is_region_transparent = is_current_transparent;
20✔
2068
      region_start = current_char;
20✔
2069
      region_count = 1;
20✔
2070
    }
2071

2072
    ++current_char;
106✔
2073
  }
2074

2075
  // Handle the final region
2076
  if ( is_region_transparent || line_covered )
64✔
2077
    putMultiLayerAreaLine (dst_char, region_count, pos);
44✔
2078
  else
2079
    putAreaLine (*region_start, *dst_char, region_count);
20✔
2080
}
2081

2082
//----------------------------------------------------------------------
2083
inline void FVTerm::addAreaLineWithTransparency ( FChar_const_iterator src_char
174✔
2084
                                                , FChar_iterator dst_char
2085
                                                , const int length ) const
2086
{
2087
  if ( length == 0 )
174✔
2088
    return;
×
2089

2090
  const auto handle_region = \
2091
      [this, &dst_char] ( bool transparent
310✔
2092
                        , int count
2093
                        , FChar_const_iterator start )
2094
  {
2095
    if ( transparent )
310✔
2096
      addTransparent (start, dst_char, count);
219✔
2097
    else
2098
      putNonTransparent (start, dst_char, count);
91✔
2099
  };
484✔
2100

2101
  const auto end_char = src_char + length;
174✔
2102
  auto region_start = src_char;
174✔
2103
  int region_count{1};
174✔
2104
  bool is_region_transparent{isFCharTransparent(*src_char)};
174✔
2105

2106
  for (auto current_char = src_char + 1; current_char < end_char; ++current_char)
1,395✔
2107
  {
2108
    const bool is_current_char_transparent = isFCharTransparent(*current_char);
1,221✔
2109

2110
    if ( is_current_char_transparent == is_region_transparent )
1,221✔
2111
      ++region_count;
1,085✔
2112
    else
2113
    {
2114
      handle_region (is_region_transparent, region_count, region_start);
136✔
2115
      // Reset for the next region
2116
      is_region_transparent = is_current_char_transparent;
136✔
2117
      region_start = current_char;
136✔
2118
      region_count = 1;
136✔
2119
    }
2120
  }
2121

2122
  // Handle the final region
2123
  handle_region (is_region_transparent, region_count, region_start);
174✔
2124
}
2125

2126
//----------------------------------------------------------------------
2127
inline void FVTerm::addTransparentAreaLine ( FChar_const_iterator& src_char
219✔
2128
                                           , FChar_iterator& dst_char
2129
                                           , const int length ) const
2130
{
2131
  auto src = src_char;
219✔
2132
  auto dst = dst_char;
219✔
2133
  auto end = src + length;
219✔
2134

2135
  while ( src < end )  // column loop
1,053✔
2136
  {
2137
    addTransparentAreaChar (*src, *dst);
834✔
2138
    ++src;  // src character
834✔
2139
    ++dst;  // dst character
834✔
2140
  }
2141
}
219✔
2142

2143
//----------------------------------------------------------------------
2144
inline void FVTerm::addTransparentAreaChar (const FChar& src_char, FChar& dst_char) const
834✔
2145
{
2146
  if ( src_char.attr.bit.transparent )  // Transparent
834✔
2147
    return;  // Leave character on vterm untouched
548✔
2148

2149
  if ( src_char.attr.bit.color_overlay )  // Color overlay
286✔
2150
  {
2151
    // Get covered character + add the current color
2152
    dst_char.color.data = src_char.color.data;
134✔
2153
    dst_char.attr.data = src_char.attr.data & ~internal::var::color_overlay_mask;
134✔
2154

2155
    if ( isTransparentInvisible(dst_char) )
134✔
2156
      dst_char.ch[0] = L' ';
9✔
2157

2158
    return;
134✔
2159
  }
2160

2161
  if ( src_char.attr.bit.inherit_background )
152✔
2162
  {
2163
    // Add the covered background to this character
2164
    auto bg_color = dst_char.color.pair.bg;
152✔
2165
    dst_char = src_char;
152✔
2166
    dst_char.color.pair.bg = bg_color;
152✔
2167
    dst_char.attr.data &= ~internal::var::print_reset_mask;
152✔
2168
    return;
152✔
2169
  }
2170

2171
  // Default
2172
  dst_char = src_char;
×
2173
}
2174

2175
//----------------------------------------------------------------------
2176
auto FVTerm::clearFullArea (FTermArea* area, FChar& fillchar) const -> bool
2✔
2177
{
2178
  // Clear area
2179
  std::fill (area->data.begin(), area->data.end(), fillchar);
2✔
2180

2181
  if ( area != vdesktop.get() )  // Is the area identical to the desktop?
2✔
2182
    return false;
×
2183

2184
  // Try to clear the terminal rapidly with a control sequence
2185
  if ( foutput->clearTerminal (fillchar.ch[0]) )
2✔
2186
  {
2187
    fillchar.attr.bit.printed = true;
2✔
2188
    std::fill (vterm->data.begin(), vterm->data.end(), fillchar);
2✔
2189
    saveCurrentVTerm();
2✔
2190
  }
2191
  else
2192
  {
NEW
2193
    auto vdesktop_changes = vdesktop->changes_in_line.begin();
×
2194
    const auto vdesktop_changes_end = vdesktop_changes
NEW
2195
                                    + vdesktop->size.height;
×
2196

2197
    while ( vdesktop_changes < vdesktop_changes_end )
×
2198
    {
2199
      vdesktop_changes->xmin = 0;
×
2200
      vdesktop_changes->xmax = uInt(vdesktop->size.width) - 1;
×
2201
      vdesktop_changes->trans_count = 0;
×
2202
      ++vdesktop_changes;
×
2203
    }
2204

2205
    vdesktop->changes_in_row = {0, uInt(vdesktop->size.height - 1)};
×
2206
    vdesktop->has_changes = true;
×
2207
  }
2208

2209
  return true;
2✔
2210
}
2211

2212
//----------------------------------------------------------------------
2213
void FVTerm::clearAreaWithShadow (FTermArea* area, const FChar& fillchar) const noexcept
1✔
2214
{
2215
  FChar t_char = fillchar;
1✔
2216
  t_char.ch[0] = L'\0';
1✔
2217
  t_char.attr.bit.transparent = true;
1✔
2218
  t_char.attr.bit.char_width = 0;
1✔
2219
  const auto total_width = getFullAreaWidth(area);
1✔
2220
  auto area_pos = area->data.begin();
1✔
2221
  auto shadow_begin = area->data.begin() + area->size.width;
1✔
2222

2223
  for (auto y{0}; y < area->size.height; y++)
21✔
2224
  {
2225
    // Clear area
2226
    std::fill (area_pos, area_pos + area->size.width, fillchar);
20✔
2227
    // Make right shadow transparent
2228
    std::fill (shadow_begin, shadow_begin + area->shadow.width, t_char);
20✔
2229
    area_pos += total_width;
20✔
2230
    shadow_begin += total_width;
20✔
2231
  }
2232

2233
  // Make bottom shadow transparent
2234
  area_pos = area->data.begin() + (total_width * area->size.height);
1✔
2235

2236
  for (auto y{0}; y < area->shadow.height; y++)
2✔
2237
  {
2238
    std::fill (area_pos, area_pos + total_width, t_char);
1✔
2239
    area_pos += total_width;
1✔
2240
  }
2241
}
1✔
2242

2243
//----------------------------------------------------------------------
2244
inline auto FVTerm::printWrap (FTermArea* area, FChar_iterator& ac) const -> bool
3,392✔
2245
{
2246
  bool end_of_area{false};
3,392✔
2247
  const int& width  = area->size.width;
3,392✔
2248
  const int& height = area->size.height;
3,392✔
2249
  const int& rsh    = area->shadow.width;
3,392✔
2250
  const int& bsh    = area->shadow.height;
3,392✔
2251

2252
  // Line break at right margin
2253
  if ( area->cursor.x > width + rsh )
3,392✔
2254
  {
2255
    area->cursor.x = 1;
×
2256
    area->cursor.y++;
×
2257
    ac = area->getFCharIterator(area->cursor.x - 1, area->cursor.y - 1);
×
2258
  }
2259

2260
  // Prevent up scrolling
2261
  if ( area->cursor.y > height + bsh )
3,392✔
2262
  {
2263
    area->cursor.y--;
×
2264
    ac = area->getFCharIterator(area->cursor.x - 1, area->cursor.y - 1);
×
2265
    end_of_area = true;
×
2266
  }
2267

2268
  return end_of_area;
3,392✔
2269
}
2270

2271
//----------------------------------------------------------------------
2272
inline auto FVTerm::interpretControlCodes ( FTermArea* area
3,448✔
2273
                                          , FChar_iterator& ac
2274
                                          , const FChar& term_char ) const noexcept -> bool
2275
{
2276
  switch ( term_char.ch.unicode_data[0] )
3,448✔
2277
  {
2278
    case L'\n':
4✔
2279
      area->cursor.y++;
4✔
2280
      // fall through
2281
    case L'\r':
22✔
2282
      area->cursor.x = 1;
22✔
2283
      break;
22✔
2284

2285
    case L'\t':
16✔
2286
      area->cursor.x += tabstop - ((area->cursor.x - 1) % tabstop);
16✔
2287
      break;
16✔
2288

2289
    case L'\b':
6✔
2290
      area->cursor.x--;
6✔
2291
      break;
6✔
2292

2293
    case L'\a':
2✔
2294
      foutput->beep();
2✔
2295
      break;
2✔
2296

2297
    default:
3,402✔
2298
      return false;
3,402✔
2299
  }
2300

2301
  ac = area->getFCharIterator(area->cursor.x - 1, area->cursor.y - 1);
46✔
2302
  return true;
46✔
2303
}
2304

2305
//----------------------------------------------------------------------
2306
inline auto FVTerm::printCharacter ( FTermArea* area
3,448✔
2307
                                   , FChar_iterator& ac
2308
                                   , const FChar& term_char ) const noexcept -> int
2309
{
2310
  if ( ! area )
3,448✔
2311
    return -1;  // No area
×
2312

2313
  if ( interpretControlCodes(area, ac, term_char) )
3,448✔
2314
    return 0;  // No printable character
46✔
2315

2316
  if ( ! area->isPrintPositionInsideArea() || printWrap(area, ac) )
3,402✔
2317
    return -1;  // Cursor position out of range or end of area reached
10✔
2318

2319
  // Printing term_char on area at the current cursor position
2320
  auto char_width = printCharacterOnCoordinate (area, ac, term_char);
3,392✔
2321

2322
  if ( char_width == 0 && ! term_char.attr.bit.fullwidth_padding )
3,392✔
2323
    return 0;
2✔
2324

2325
  area->cursor.x++;
3,390✔
2326
  ++ac;
3,390✔
2327
  area->has_changes = true;
3,390✔
2328

2329
  // Line break at right margin
2330
  if ( area->cursor.x > getFullAreaWidth(area) )
3,390✔
2331
  {
2332
    area->cursor.x = 1;
129✔
2333
    area->cursor.y++;
129✔
2334
  }
2335
  else if ( char_width == 2 )
3,261✔
2336
  {
2337
    printPaddingCharacter (area, term_char);
2✔
2338
    ++ac;
2✔
2339
  }
2340

2341
  // Prevent up scrolling
2342
  if ( area->cursor.y > getFullAreaHeight(area) )
3,390✔
2343
    area->cursor.y--;
16✔
2344

2345
  return 1;
3,390✔
2346
}
2347

2348
//----------------------------------------------------------------------
2349
inline auto FVTerm::printCharacterOnCoordinate ( FTermArea* area
3,392✔
2350
                                               , const FChar_iterator& ac
2351
                                               , const FChar& ch ) const noexcept -> std::size_t
2352
{
2353
  const auto ax = unsigned(area->cursor.x - 1);
3,392✔
2354
  const auto ay = unsigned(area->cursor.y - 1);
3,392✔
2355

2356
  if ( *ac == ch )  // compare with an overloaded operator
3,392✔
2357
    return ac->attr.bit.char_width;
282✔
2358

2359
  const bool trans_old =
2360
      (ac->attr.data & internal::var::print_transparent_mask) != 0;
3,110✔
2361
  const bool trans_new =
3,110✔
2362
      (ch.attr.data & internal::var::print_transparent_mask) != 0;
3,110✔
2363
  const auto trans_changed = int(trans_new) - int(trans_old);
3,110✔
2364

2365
  if ( trans_changed != 0 )
3,110✔
2366
    area->changes_in_line[ay].trans_count += uInt(trans_changed);
308✔
2367

2368
  // copy character to area
2369
  *ac = ch;
3,110✔
2370

2371
  auto current_width = ac->attr.bit.char_width;
3,110✔
2372

2373
  if ( current_width == 0 )
3,110✔
2374
  {
2375
    const auto new_char_width = getColumnWidth(ac->ch[0]);
19✔
2376

2377
    if ( new_char_width == 0 )
19✔
2378
      return 0;
3✔
2379

2380
    addColumnWidth(*ac, new_char_width);  // Add column width
16✔
2381
    current_width = uInt8(new_char_width);
16✔
2382
  }
2383

2384
  area->updateAreaChanges (ax, ay, current_width);
3,107✔
2385
  return current_width;
3,107✔
2386
}
2387

2388
//----------------------------------------------------------------------
2389
inline void FVTerm::printPaddingCharacter ( FTermArea* area
2✔
2390
                                          , const FChar& term_char ) const
2391
{
2392
  // Creates a padding-character from the current character (term_char)
2393
  // and prints it. It is a placeholder for the column after
2394
  // a full-width character.
2395

2396
  FChar pc{term_char};  // Copy character to padding character
2✔
2397

2398
  if ( area->encoding == Encoding::UTF8 )
2✔
2399
  {
2400
    pc.ch[0] = L'\0';
1✔
2401
    pc.attr.bit.fullwidth_padding = true;
1✔
2402
    pc.attr.bit.char_width = 0;
1✔
2403
  }
2404
  else
2405
  {
2406
    pc.ch[0] = L'.';
1✔
2407
    pc.ch[1] = L'\0';
1✔
2408
    pc.attr.bit.char_width = 1;
1✔
2409
  }
2410

2411
  // Print the padding-character
2412
  print (area, pc);
2✔
2413
}
2✔
2414

2415
//----------------------------------------------------------------------
2416
inline void FVTerm::putNonTransparent ( FChar_const_iterator start_char
91✔
2417
                                      , FChar_iterator& dst_char
2418
                                      , int& non_trans_count ) const
2419
{
2420
  if ( non_trans_count == 0 )
91✔
2421
    return;
×
2422

2423
  putAreaLine(*start_char, *dst_char, non_trans_count);
91✔
2424
  dst_char += non_trans_count;  // dst character
91✔
2425
  non_trans_count = 0;
91✔
2426
}
2427

2428
//----------------------------------------------------------------------
2429
inline void FVTerm::addTransparent ( FChar_const_iterator start_char
219✔
2430
                                   , FChar_iterator& dst_char
2431
                                   , int& trans_count ) const
2432
{
2433
  if ( trans_count == 0 )
219✔
2434
    return;
×
2435

2436
  addTransparentAreaLine (start_char, dst_char, trans_count);
219✔
2437
  dst_char += trans_count;  // dst character
219✔
2438
  trans_count = 0;
219✔
2439
}
2440

2441
//----------------------------------------------------------------------
2442
inline void FVTerm::addVDesktopToListIfExists (FTermAreaList& list) const
18✔
2443
{
2444
  // Adds the virtual desktop area, if it exists
2445

2446
  if ( vdesktop )
18✔
2447
    list.push_back(vdesktop.get());
18✔
2448
}
18✔
2449

2450
//----------------------------------------------------------------------
2451
inline void FVTerm::determineCoveredAreas (FTermArea* src) const
18✔
2452
{
2453
  const auto* vterm_win_list = getWindowList();
18✔
2454
  covered_areas_buffer.clear();
18✔
2455
  resetLineCoveredState(src);
18✔
2456

2457
  if ( ! vterm_win_list || vterm_win_list->empty() )
18✔
2458
  {
2459
    addVDesktopToListIfExists(covered_areas_buffer);
10✔
2460
    return;
10✔
2461
  }
2462

2463
  const auto end = vterm_win_list->crend();
8✔
2464
  const auto src_layer = src->layer;
8✔
2465

2466
  // Areas from top to bottom
2467
  for (auto iter = vterm_win_list->crbegin(); iter < end; ++iter)
16✔
2468
  {
2469
    const auto& vterm_obj = *iter;
8✔
2470

2471
    if ( ! vterm_obj )
8✔
2472
      continue;
8✔
2473

2474
    const auto* win = vterm_obj->getVWin();
8✔
2475

2476
    if ( ! win || ! win->visible || win->layer < 1
8✔
2477
      || ! win->isOverlapped(src) )
16✔
2478
      continue;
×
2479

2480
    covered_areas_buffer.push_back(win);
8✔
2481

2482
    if ( win->layer <= src_layer )
8✔
2483
      continue;
8✔
2484

2485
    determineLineCoveredState(win, src);
×
2486
  }
2487

2488
  addVDesktopToListIfExists(covered_areas_buffer);
8✔
2489
}
2490

2491
//----------------------------------------------------------------------
2492
inline void FVTerm::resetLineCoveredState (FTermArea* src) const
18✔
2493
{
2494
  auto changes = src->changes_in_line.begin();
18✔
2495
  const auto changes_end = src->changes_in_line.end();
18✔
2496

2497
  // Unrolled loop for better performance
2498
  while ( changes + 4 < changes_end )
114✔
2499
  {
2500
    changes[0].covered = false;
96✔
2501
    changes[1].covered = false;
96✔
2502
    changes[2].covered = false;
96✔
2503
    changes[3].covered = false;
96✔
2504
    changes += 4;
96✔
2505
  }
2506

2507
  while ( changes < changes_end )  // Handle remaining lines
68✔
2508
  {
2509
    changes->covered = false;
50✔
2510
    ++changes;
50✔
2511
  }
2512
}
18✔
2513

2514
//----------------------------------------------------------------------
2515
inline void FVTerm::determineLineCoveredState ( const FTermArea* const win
×
2516
                                              , FTermArea* src ) const noexcept
2517
{
2518
  // Requirements: The two areas have been checked for overlap
2519

2520
  const int win_height = win->minimized ? win->min_size.height
×
2521
                                        : getFullAreaHeight(win);
×
2522
  const int src_height = src->minimized ? src->min_size.height
×
2523
                                        : getFullAreaHeight(src);
×
2524
  const int win_y_min = win->position.y;
×
2525
  const int win_y_max = win_y_min + win_height;
×
2526
  const int src_y_min = src->position.y;
×
2527
  const int src_y_max = src_y_min + src_height;
×
2528
  const int overlap_start = std::max(win_y_min, src_y_min);
×
2529
  const int overlap_end = std::min(win_y_max, src_y_max);
×
NEW
2530
  const auto start = overlap_start - src_y_min;
×
NEW
2531
  const auto end = overlap_end - src_y_min;
×
NEW
2532
  auto changes_base = src->changes_in_line.begin();
×
NEW
2533
  auto changes = changes_base + start;
×
NEW
2534
  const auto changes_end = changes_base + end;
×
2535

2536
  // Unrolled loop for better performance
2537
  while ( changes + 4 <= changes_end )
×
2538
  {
2539
    changes[0].covered = true;
×
2540
    changes[1].covered = true;
×
2541
    changes[2].covered = true;
×
2542
    changes[3].covered = true;
×
2543
    changes += 4;
×
2544
  }
2545

2546
  while ( changes < changes_end )  // Handle remaining lines
×
2547
  {
2548
    changes->covered = true;
×
2549
    ++changes;
×
2550
  }
2551
}
×
2552

2553
//----------------------------------------------------------------------
2554
auto FVTerm::canUpdateTerminalNow() const -> bool
73✔
2555
{
2556
  // Check if terminal updates were stopped, application is stopping,
2557
  // VTerm has no changes, or the drawing is not completed
2558

2559
  return  ! FVTerm::areTerminalUpdatesPaused()
73✔
2560
       && ! FApplication::isQuit()
73✔
2561
       && ( foutput->isFlushTimeout() || FVTerm::isTerminalUpdateForced() )
1✔
2562
       && FVTerm::hasPendingTerminalUpdates()
1✔
2563
       && FVTerm::isDrawingFinished();
146✔
2564
}
2565

2566
//----------------------------------------------------------------------
2567
auto FVTerm::hasPendingUpdates (const FTermArea* area) noexcept -> bool
99✔
2568
{
2569
  return (area && area->has_changes);
99✔
2570
}
2571

2572
}  // 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