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

gansm / finalcut / #760

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

push

travis-ci

gansm
Reduce pointer arithmetic and use safe iterators more often

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

25 existing lines in 6 files now uncovered.

37622 of 54474 relevant lines covered (69.06%)

243.8 hits per line

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

85.45
/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 getByte1TransparentMask() noexcept -> uInt8
49
{
50
  FCharAttribute mask{};
51
  mask.transparent = true;
52
  mask.color_overlay = true;
53
  mask.inherit_background = true;
54
  return getFAttributeByte(mask, 1);
55
}
56

57
constexpr auto getByte1Transparent() noexcept -> uInt8
58
{
59
  FCharAttribute mask{};
60
  mask.transparent = true;
61
  return getFAttributeByte(mask, 1);
62
}
63

64
constexpr auto getByte1ColorOverlay() noexcept -> uInt8
65
{
66
  FCharAttribute mask{};
67
  mask.color_overlay = true;
68
  return getFAttributeByte(mask, 1);
69
}
70

71
constexpr auto getByte1InheritBackground() noexcept -> uInt8
72
{
73
  FCharAttribute mask{};
74
  mask.inherit_background = true;
75
  return getFAttributeByte(mask, 1);
76
}
77

78
constexpr auto getByte1PrintTransparentMask() noexcept -> uInt8
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 getFAttributeByte(mask, 1);
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 b1_transparent_mask = getByte1TransparentMask();
112
  static constexpr auto b1_transparent = getByte1Transparent();
113
  static constexpr auto b1_color_overlay = getByte1ColorOverlay();
114
  static constexpr auto b1_inherit_background = getByte1InheritBackground();
115
  static constexpr auto b1_print_transparent_mask = getByte1PrintTransparentMask();
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 uInt8  var::b1_transparent_mask;
122
constexpr uInt8  var::b1_transparent;
123
constexpr uInt8  var::b1_print_transparent_mask;
124
constexpr uInt32 var::print_reset_mask;
125
constexpr uInt32 var::color_overlay_mask;
126

127
}  // namespace internal
128

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

137
using TransparentInvisibleLookupMap = std::unordered_set<wchar_t>;
138

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

156
  return *trans_inv_lookup;
1✔
157
}
158

159
//----------------------------------------------------------------------
160
// class FVTerm
161
//----------------------------------------------------------------------
162

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

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

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

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

191

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

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

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

209
  return {0, 0};  // Fallback coordinates
×
210
}
211

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

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

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

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

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

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

255
  auto iter = print_area->preproc_list.cbegin();
1✔
256

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

262
    ++iter;
×
263
  }
264

265
  return false;
×
266
}
267

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

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

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

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

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

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

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

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

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

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

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

332
  if ( terminal_updated )
73✔
333
    saveCurrentVTerm();
1✔
334

335
  return terminal_updated;
73✔
336
}
337

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

348
  if ( xmin > xmax )  // No changes
24✔
349
    return;
×
350

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

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

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

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

375
    --last;
28✔
376
    --last_old;
28✔
377
  }
378
}
379

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

387
  if ( ! instance || ! print_area )
1✔
388
    return;
×
389

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

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

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

405
  auto iter = print_area->preproc_list.cbegin();
1✔
406

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

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

422
  vterm_buffer.print(string);
973✔
423
  return print (vterm_buffer);
973✔
424
}
425

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

432
  vterm_buffer.print(string);
2✔
433
  return print (area, vterm_buffer);
2✔
434
}
435

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

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

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

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

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

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

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

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

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

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

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

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

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

500
    len++;
3,422✔
501
  }
502

503
  return len;
983✔
504
}
505

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

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

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

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

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

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

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

554

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

561
  if ( print_area )
1,083✔
562
    return print_area;
1,064✔
563

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

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

576
  return vdesktop.get();
3✔
577
}
578

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

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

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

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

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

606
  assert ( isAreaValid(shadowbox) );
139✔
607

608
  if ( ! area )
139✔
609
    return;
×
610

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

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

623
  if ( ! tryResizeArea(area, full_width, full_height) )
134✔
624
    return;
×
625

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

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

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

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

646
  const auto* vterm_win_list = getWindowList();
13✔
647

648
  if ( ! vterm_win_list || vterm_win_list->empty() )
13✔
649
    return;
×
650

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

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

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

662
    auto* win = vterm_obj->getVWin();
31✔
663

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

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

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

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

695
  vterm->input_cursor_visible = false;
7✔
696
  return false;
7✔
697
}
698

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

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

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

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

722
  if ( ! area )
14✔
723
    return;
×
724

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

728
  if ( ax < 0 || ay < 0 )
14✔
729
    return;
3✔
730

731
  const int y_end  = std::min(vterm->size.height - ay, area->size.height);
11✔
732
  const int length = std::min(vterm->size.width - ax, area->size.width);
11✔
733
  const auto area_width = unsigned(getFullAreaWidth(area));
11✔
734
  const auto vterm_width = unsigned(getFullAreaWidth(vterm.get()));
11✔
735
  auto line_changes = &area->changes_in_line[0];
11✔
736
  auto* ac = &area->data[0];  // area character
11✔
737
  const auto* tc = &vterm->data[ay * vterm_width];  // terminal character
11✔
738

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

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

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

759
  if ( ! area )
13✔
760
    return;
3✔
761

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

769
  if ( x < 0 || y < 0 )
13✔
770
    return;
3✔
771

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

781
  if ( length < 1 )
10✔
782
    return;
×
783

784
  auto* line_changes = &area->changes_in_line[unsigned(dy)];
10✔
785

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

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

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

806
  if ( ! area || ! area->visible )
91✔
807
    return;
19✔
808

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

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

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

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

823
  if ( line_changes_batch.empty() )
88✔
824
    return;
16✔
825

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

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

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

839
  if ( ! area || ! area->visible )
22✔
840
    return;
1✔
841

842
  copyArea (vterm.get(), pos, area);
21✔
843
}
844

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

850
  if ( ! dst || ! src )
21✔
851
    return;
×
852

853
  if ( dst == vterm.get() )
21✔
854
    skip_one_vterm_update = true;
21✔
855

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

866
  if ( length < 1 || y_end < 1 )
21✔
867
    return;
3✔
868

869
  const auto* src_changes = &src->changes_in_line[0];
18✔
870
  auto* dst_changes = &dst->changes_in_line[unsigned(ay)];
18✔
871
  const auto* sc = &src->data[src_width * ot +  ol];  // src character ptr
18✔
872
  auto* dc = &dst->data[dst_width * ay + ax];  // dst character ptr
18✔
873

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

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

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

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

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

908
  const auto* vterm_win_list = getWindowList();
6✔
909

910
  if ( ! vterm_win_list || vterm_win_list->empty() )
6✔
911
    return;
×
912

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

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

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

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

932
  const int y_max = area->size.height - 1;
8✔
933
  const int x_max = area->size.width - 1;
8✔
934
  auto line_changes = &area->changes_in_line[0];
8✔
935
  const auto area_width = unsigned(getFullAreaWidth(area));
8✔
936
  const auto* sc = &area->data[area_width];  // source character
8✔
937
  auto* dc = &area->data[0];  // destination character
8✔
938

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

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

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

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

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

975
  const int y_max = area->size.height - 1;
12✔
976
  const int x_max = area->size.width - 1;
12✔
977
  auto line_changes = &area->changes_in_line[unsigned(y_max)];
12✔
978
  const auto area_width = unsigned(getFullAreaWidth(area));
12✔
979
  const auto* sc = &area->data[area_width * unsigned(y_max - 1)];  // source character
12✔
980
  auto* dc = &area->data[area_width * unsigned(y_max)];  // destination character
12✔
981

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

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

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

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

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

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

1028
  const auto width = uInt(getFullAreaWidth(area));
3✔
1029

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

1038
  auto line_changes = &area->changes_in_line[0];
1✔
1039

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

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

1054
    ++line_changes;
20✔
1055
  }
1056

1057
  line_changes = &area->changes_in_line[unsigned(area->size.height)];
1✔
1058

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

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

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

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

1085
  if ( foutput->hasTerminalResized() )
70✔
1086
    return false;
×
1087

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

1094
  // Update the visible terminal
1095
  return updateTerminal();
70✔
1096
}
1097

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

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

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

1120

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

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

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

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

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

1152
    if ( ! v_win )
×
1153
      continue;
×
1154

1155
    v_win->encoding = encoding;
×
1156

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

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

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

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

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

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

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

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

1211
  const auto* vterm_win_list{getWindowList()};
3✔
1212

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

1217
  const auto pos_x = pos.getX();
×
1218
  const auto pos_y = pos.getY();
×
1219

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

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

1227
    if ( ! win || ! win->visible )
×
1228
      continue;
×
1229

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

1236
    const int win_x_min = win->position.x;
×
1237
    const int win_y_min = win->position.y;
×
1238

1239
    if ( pos_x < win_x_min || pos_y < win_y_min )
×
1240
      continue;
×
1241

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

1247
    if ( pos_x >= win_x_max || pos_y >= win_y_max )
×
1248
      continue;
×
1249

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

1257
    if ( character.attr.bit.transparent )
×
1258
      continue;
×
1259

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

1266
  return is_covered;
×
1267
}
1268

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

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

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

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

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

1308
  if ( needsWidthResize(area, width) )
3✔
1309
    return resizeTextArea(area, width * height);
3✔
1310

1311
  return false;
×
1312
}
1313

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

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

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

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

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

1348
  processOverlappingWindows (area, *vterm_win_list);
34✔
1349
}
1350

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

1357
  if ( ! area )
34✔
1358
    return;
×
1359

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

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

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

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

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

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

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

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

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

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

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

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

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

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

1438
  updateVTermDesktop();
50✔
1439

1440
  if ( ! vterm_window_list || vterm_window_list->empty() )
50✔
1441
    return;
32✔
1442

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

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

1450
    updateVTermWindow(v_win);
47✔
1451
  }
1452
}
1453

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

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

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

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

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

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

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

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

1520
  return true;
88✔
1521
}
1522

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

1531
  auto line_changes = &area->changes_in_line[unsigned(geo.y_start)];
88✔
1532

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

1644
  foutput->flush();  // Empty buffer before scrolling
3✔
1645

1646
  if ( ! foutput->scrollTerminalForward() )
3✔
1647
    return;
×
1648

1649
  // Avoid update lines from 0 to (height - 2)
1650
  auto* vdesktop_changes = &vdesktop->changes_in_line[0];
3✔
1651
  const auto* vdesktop_changes_end = vdesktop_changes
1652
                                   + unsigned(vdesktop->size.height - 1);
3✔
1653

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

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

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

1672
  foutput->flush();  // Empty buffer before scrolling
7✔
1673

1674
  if ( ! foutput->scrollTerminalReverse() )
7✔
1675
    return;
×
1676

1677
  // avoid update lines from 1 to (height - 1)
1678
  auto* vdesktop_changes = &vdesktop->changes_in_line[1];
7✔
1679
  const auto* vdesktop_changes_end = vdesktop_changes
1680
                                   + unsigned(vdesktop->size.height);
7✔
1681

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

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

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

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

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

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

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

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

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

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

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

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

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

1763
//----------------------------------------------------------------------
1764
constexpr auto FVTerm::isFCharTransparent (const FChar& fchar) noexcept -> bool
2,321✔
1765
{
1766
  return (fchar.attr.byte[1] & internal::var::b1_transparent_mask) != 0;
2,321✔
1767
}
1768

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

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

1784
  // Initialize the printing attribute
1785
  FVTermAttribute::initAttribute();
33✔
1786

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

1867
  overlay_line_buffer.clear();  // Clear buffer
44✔
1868

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

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

1886
    // Precalculate array indexing values for getFChar
1887
    const auto x = term_x - win->position.x;
88✔
1888
    const auto y = term_y - win->position.y;
88✔
1889
    const auto width = getFullAreaWidth(win);
88✔
1890
    const auto index = unsigned(y) * unsigned(width) + unsigned(x);
88✔
1891

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

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

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

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

1913
      if ( char_search == SearchState::ready )
252✔
1914
        continue;
×
1915

1916
      const auto transparency = win_char->attr.byte[1]
252✔
1917
                              & internal::var::b1_transparent_mask;
252✔
1918

1919
      if ( transparency  == internal::var::b1_transparent )  // Transparent
252✔
1920
      {
1921
        if ( char_search == SearchState::start )
126✔
1922
          char_search = SearchState::printable;
126✔
1923

1924
        continue;
126✔
1925
      }
1926

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

1940
        continue;
×
1941
      }
1942

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

1956
        continue;
×
1957
      }
1958

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

1973
      char_search = SearchState::ready;
126✔
1974
    }
1975
  }
1976
}
1977

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

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

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

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

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

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

2031
        pos.x_ref() += int(region_count);
20✔
2032
        dst_char += region_count;
20✔
2033

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

2040
      ++current_char;
80✔
2041
    }
2042
  }
2043

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

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

2060
      pos.x_ref() += int(region_count);
20✔
2061
      dst_char += region_count;
20✔
2062

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

2069
    ++current_char;
106✔
2070
  }
2071

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

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

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

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

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

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

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

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

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

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

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

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

2155
    return;
134✔
2156
  }
2157

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

2168
  // Default
2169
  dst_char = src_char;
×
2170
}
2171

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

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

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

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

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

2206
  return true;
2✔
2207
}
2208

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

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

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

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

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

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

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

2265
  return end_of_area;
3,392✔
2266
}
2267

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

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

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

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

2294
    default:
3,402✔
2295
      return false;
3,402✔
2296
  }
2297

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

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

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

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

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

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

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

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

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

2342
  return 1;
3,390✔
2343
}
2344

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

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

2356
  const bool trans_old =
2357
      (ac->attr.byte[1] & internal::var::b1_print_transparent_mask) != 0;
3,110✔
2358
  const bool trans_new =
2359
      (ch.attr.byte[1] & internal::var::b1_print_transparent_mask) != 0;
3,110✔
2360
  const auto trans_changed = int(trans_new) - int(trans_old);
3,110✔
2361

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

2365
  // copy character to area
2366
  *ac = ch;
3,110✔
2367

2368
  auto current_width = ac->attr.bit.char_width;
3,110✔
2369

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

2374
    if ( new_char_width == 0 )
19✔
2375
      return 0;
3✔
2376

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

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

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

2393
  FChar pc{term_char};  // Copy character to padding character
2✔
2394

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

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

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

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

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

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

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

2443
  if ( vdesktop )
18✔
2444
    list.push_back(vdesktop.get());
18✔
2445
}
18✔
2446

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

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

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

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

2468
    if ( ! vterm_obj )
8✔
2469
      continue;
8✔
2470

2471
    const auto* win = vterm_obj->getVWin();
8✔
2472

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

2477
    covered_areas_buffer.push_back(win);
8✔
2478

2479
    if ( win->layer <= src_layer )
8✔
2480
      continue;
8✔
2481

UNCOV
2482
    determineLineCoveredState(win, src);
×
2483
  }
2484

2485
  addVDesktopToListIfExists(covered_areas_buffer);
8✔
2486
}
2487

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

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

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

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

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

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

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

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

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

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

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