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

gansm / finalcut / #767

09 Feb 2026 11:49PM UTC coverage: 69.07%. Remained the same
#767

push

travis-ci

gansm
More iterators and further code improvements

126 of 129 new or added lines in 7 files covered. (97.67%)

161 existing lines in 3 files now uncovered.

37625 of 54474 relevant lines covered (69.07%)

250.26 hits per line

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

85.69
/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
//----------------------------------------------------------------------
UNCOV
187
auto FVTerm::operator << (const FVTermBuffer& buffer) noexcept -> FVTerm&
×
188
{
189
  print (buffer);
×
UNCOV
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

UNCOV
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

UNCOV
264
    ++iter;
×
265
  }
266

UNCOV
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✔
UNCOV
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✔
UNCOV
387
    getPrintArea();
×
388

389
  if ( ! instance || ! print_area )
1✔
UNCOV
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✔
UNCOV
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
UNCOV
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
//----------------------------------------------------------------------
UNCOV
469
auto FVTerm::print (const FVTermBuffer& buffer) noexcept -> int
×
470
{
471
  if ( buffer.isEmpty() )
×
UNCOV
472
    return -1;
×
473

474
  auto* area = getPrintArea();
×
UNCOV
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
  {
UNCOV
574
    print_area = child_print_area;
×
UNCOV
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✔
UNCOV
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✔
UNCOV
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✔
UNCOV
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✔
UNCOV
646
    return;
×
647

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

650
  if ( ! vterm_win_list || vterm_win_list->empty() )
13✔
UNCOV
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✔
UNCOV
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✔
UNCOV
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, unsigned(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✔
UNCOV
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), unsigned(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✔
UNCOV
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[0], &dc[0], length, {ax, ay + y}, src_changes->covered);
64✔
885
    }
886
    else
887
    {
888
      // Line has only covered characters
889
      putAreaLine (sc[0], *dc, unsigned(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✔
UNCOV
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✔
UNCOV
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, unsigned(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✔
UNCOV
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, unsigned(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
  {
UNCOV
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✔
UNCOV
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
UNCOV
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✔
UNCOV
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
  {
UNCOV
1152
    auto v_win = vterm_obj->getVWin();
×
1153

1154
    if ( ! v_win )
×
UNCOV
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 )
×
UNCOV
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

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

1222
  auto is_covered{CoveredState::None};  // Initial state: no coverage
×
UNCOV
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 )
×
UNCOV
1230
      continue;
×
1231

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

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

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

1244
    const int current_height = win->minimized ? win->min_size.height
×
UNCOV
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 )
×
UNCOV
1250
      continue;
×
1251

1252
    // Position is covered by this window - check for transparency
UNCOV
1253
    const auto delta_x = pos_x - win->position.x;
×
UNCOV
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 )
×
UNCOV
1260
      continue;
×
1261

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

UNCOV
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

UNCOV
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✔
UNCOV
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✔
UNCOV
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
  {
UNCOV
1477
    passChangesToOverlap(v_win);
×
UNCOV
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 = unsigned(vterm->size.width);
88✔
1490
  const auto vterm_height = unsigned(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
    static_cast<unsigned>(std::max(area_x, 0)),
264✔
1501
    getFullAreaWidth(area),
88✔
1502
    height,
1503
    static_cast<int>(vterm_width) - area_x - 1,
88✔
1504
    // Calculate actual rendering range (clipped to vterm)
1505
    std::max(0, -area_y),
264✔
1506
    std::min(static_cast<int>(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✔
UNCOV
1514
    return false;
×
1515

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

1519
  if ( geo.area_y + geo.height <= 0 )
88✔
UNCOV
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[unsigned(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
    {
UNCOV
1554
      ++line_changes;
×
UNCOV
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 = static_cast<unsigned>(line.xmin);
101✔
1586
    const auto line_xmax = static_cast<unsigned>(line.xmax);
101✔
1587
    const auto has_no_trans = line.has_no_transparency == NoTrans::Set;
101✔
1588
    const auto tx = unsigned(geo.area_x) + line_xmin;  // Global terminal x-position
101✔
1589
    const std::size_t 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 = static_cast<unsigned>(line.ypos + i);
491✔
1595
      auto line_changes = &area->changes_in_line[y];
491✔
1596
      const auto ty = unsigned(geo.area_y) + y;         // Global terminal y-position
491✔
1597
      const auto area_line_offset = y * unsigned(geo.width) + line_xmin;
491✔
1598
      const auto vterm_line_offset = ty * geo.vterm_width + tx;
491✔
1599
      const auto* ac = &area->data[area_line_offset];   // Area character
491✔
1600
      auto* tc = &vterm->data[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[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✔
UNCOV
1649
    return;
×
1650

1651
  // Avoid update lines from 0 to (height - 2)
1652
  auto* vdesktop_changes = &vdesktop->changes_in_line[0];
3✔
1653
  const auto* vdesktop_changes_end = vdesktop_changes
1654
                                   + unsigned(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✔
UNCOV
1677
    return;
×
1678

1679
  // avoid update lines from 1 to (height - 1)
1680
  auto* vdesktop_changes = &vdesktop->changes_in_line[1];
7✔
1681
  const auto* vdesktop_changes_end = vdesktop_changes
1682
                                   + unsigned(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

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

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

1736
  for (const auto& pcall : area->preproc_list)
×
1737
  {
1738
    if ( pcall->instance && pcall->instance->child_print_area )
×
UNCOV
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 std::size_t length ) const noexcept
1830
{
1831
  // copy "length" characters from area to terminal
1832

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

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

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

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

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

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

1864
  auto search_buffer = overlay_search_buffer.data();
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 + int(length) <= x_min )
88✔
UNCOV
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 = unsigned(y) * unsigned(width) + unsigned(x);
88✔
1893

1894
    // Calculate the intersection of the line with the window
1895
    const auto start_idx = std::size_t(std::max(0, x_min - term_x));
88✔
1896
    const auto end_idx = std::size_t(std::min(int(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.data(), unsigned(index), start_idx, end_idx }
88✔
1902
    );
1903
  }
1904

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

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

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

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

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

1926
        continue;
126✔
1927
      }
1928

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

UNCOV
1942
        continue;
×
1943
      }
1944

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

UNCOV
1958
        continue;
×
1959
      }
1960

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

2071
    ++current_char;
106✔
2072
  }
2073

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

2081
//----------------------------------------------------------------------
2082
inline void FVTerm::addAreaLineWithTransparency ( const FChar* src_char
174✔
2083
                                                , FChar* dst_char
2084
                                                , const std::size_t length ) const
2085
{
2086
  if ( length == 0 )
174✔
UNCOV
2087
    return;
×
2088

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

2100
  const auto* end_char{src_char + length};
174✔
2101
  const auto* region_start{src_char};
174✔
2102
  std::size_t region_count{1};
174✔
2103
  bool is_region_transparent{isFCharTransparent(*src_char)};
174✔
2104

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

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

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

2125
//----------------------------------------------------------------------
2126
inline void FVTerm::addTransparentAreaLine ( const FChar& src_char
219✔
2127
                                           , FChar& dst_char
2128
                                           , const std::size_t length ) const
2129
{
2130
  const auto* src = &src_char;
219✔
2131
  auto* dst = &dst_char;
219✔
2132
  const auto* end = src + length;
219✔
2133

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

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

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

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

2157
    return;
134✔
2158
  }
2159

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

2170
  // Default
UNCOV
2171
  dst_char = src_char;
×
2172
}
2173

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

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

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

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

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

2208
  return true;
2✔
2209
}
2210

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

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

2232
  // Make bottom shadow transparent
2233
  area_pos = &area->data[total_width * area->size.height];
1✔
2234

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

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

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

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

2267
  return end_of_area;
3,392✔
2268
}
2269

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

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

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

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

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

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

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

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

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

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

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

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

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

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

2344
  return 1;
3,390✔
2345
}
2346

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

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

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

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

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

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

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

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

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

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

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

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

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

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

2414
//----------------------------------------------------------------------
2415
inline void FVTerm::putNonTransparent ( const FChar* start_char
91✔
2416
                                      , FChar*& dst_char
2417
                                      , std::size_t& non_trans_count ) const
2418
{
2419
  if ( non_trans_count == 0 )
91✔
UNCOV
2420
    return;
×
2421

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

2427
//----------------------------------------------------------------------
2428
inline void FVTerm::addTransparent ( const FChar* start_char
219✔
2429
                                   , FChar*& dst_char
2430
                                   , std::size_t& trans_count ) const
2431
{
2432
  if ( trans_count == 0 )
219✔
UNCOV
2433
    return;
×
2434

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

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

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

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

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

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

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

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

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

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

2479
    covered_areas_buffer.push_back(win);
8✔
2480

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

UNCOV
2484
    determineLineCoveredState(win, src);
×
2485
  }
2486

2487
  addVDesktopToListIfExists(covered_areas_buffer);
8✔
2488
}
2489

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

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

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

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

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

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

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

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

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

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

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