Coverage Report

Created: 2026-11-06 11:29

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/libreoffice/vcl/headless/svpframe.cxx
Line
Count
Source
1
/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
2
/*
3
 * This file is part of the LibreOffice project.
4
 *
5
 * This Source Code Form is subject to the terms of the Mozilla Public
6
 * License, v. 2.0. If a copy of the MPL was not distributed with this
7
 * file, You can obtain one at http://mozilla.org/MPL/2.0/.
8
 *
9
 * This file incorporates work covered by the following license notice:
10
 *
11
 *   Licensed to the Apache Software Foundation (ASF) under one or more
12
 *   contributor license agreements. See the NOTICE file distributed
13
 *   with this work for additional information regarding copyright
14
 *   ownership. The ASF licenses this file to you under the Apache
15
 *   License, Version 2.0 (the "License"); you may not use this file
16
 *   except in compliance with the License. You may obtain a copy of
17
 *   the License at http://www.apache.org/licenses/LICENSE-2.0 .
18
 */
19
20
#include <comphelper/lok.hxx>
21
#include <sal/log.hxx>
22
23
#include <headless/svpframe.hxx>
24
#include <headless/svpinst.hxx>
25
#ifndef IOS
26
#include <headless/svpgdi.hxx>
27
#endif
28
#include <salsys.hxx>
29
30
#include <basegfx/vector/b2ivector.hxx>
31
#include <vcl/keycodes.hxx>
32
33
#ifndef IOS
34
#include <cairo.h>
35
#endif
36
37
SvpSalFrame* SvpSalFrame::s_pFocusFrame = nullptr;
38
39
#ifdef IOS
40
#define SvpSalGraphics AquaSalGraphics
41
#endif
42
43
SvpSalFrame::SvpSalFrame( SvpSalInstance& rInstance,
44
                          SalFrame* pParent,
45
                          SalFrameStyleFlags nSalFrameStyle ) :
46
11.0k
    m_rInstance(rInstance),
47
11.0k
    m_pParent( static_cast<SvpSalFrame*>(pParent) ),
48
11.0k
    m_nStyle( nSalFrameStyle ),
49
11.0k
    m_bVisible( false ),
50
#ifndef IOS
51
11.0k
    m_pSurface( nullptr ),
52
#endif
53
11.0k
    m_nMinWidth( 0 ),
54
11.0k
    m_nMinHeight( 0 ),
55
11.0k
    m_nMaxWidth( 0 ),
56
11.0k
    m_nMaxHeight( 0 )
57
11.0k
{
58
11.0k
#if !defined(IOS) && !defined(MACOSX) && !defined(ANDROID) && !defined(_WIN32)
59
11.0k
    m_aSystemChildData.pSalFrame    = this;
60
11.0k
#endif
61
62
11.0k
    if( m_pParent )
63
0
        m_pParent->m_aChildren.push_back( this );
64
65
11.0k
    m_rInstance.insertFrame(this);
66
67
11.0k
    SetPosSize( 0, 0, 800, 600, SAL_FRAME_POSSIZE_WIDTH | SAL_FRAME_POSSIZE_HEIGHT );
68
11.0k
}
69
70
SvpSalFrame::~SvpSalFrame()
71
6.21k
{
72
6.21k
    m_rInstance.eraseFrame(this);
73
74
6.21k
    std::vector<SvpSalFrame*> Children = m_aChildren;
75
6.21k
    for( auto& rChild : Children )
76
0
        rChild->SetParent( m_pParent );
77
6.21k
    if( m_pParent )
78
0
        std::erase(m_pParent->m_aChildren, this);
79
80
6.21k
    if( s_pFocusFrame == this )
81
0
    {
82
0
        s_pFocusFrame = nullptr;
83
        // call directly here, else an event for a destroyed frame would be dispatched
84
0
        CallCallback( SalEvent::LoseFocus, nullptr );
85
        // if the handler has not set a new focus frame
86
        // pass focus to another frame, preferably a document style window
87
0
        if( s_pFocusFrame == nullptr )
88
0
        {
89
0
            for (auto pSalFrame : m_rInstance.getFrames())
90
0
            {
91
0
                SvpSalFrame* pFrame = static_cast<SvpSalFrame*>( pSalFrame );
92
0
                if( pFrame->m_bVisible        &&
93
0
                    pFrame->m_pParent == nullptr &&
94
0
                    (pFrame->m_nStyle & (SalFrameStyleFlags::MOVEABLE |
95
0
                                         SalFrameStyleFlags::SIZEABLE |
96
0
                                         SalFrameStyleFlags::CLOSEABLE) )
97
0
                    )
98
0
                {
99
0
                    pFrame->GetFocus();
100
0
                    break;
101
0
                }
102
0
            }
103
0
        }
104
0
    }
105
6.21k
#ifndef IOS
106
6.21k
    if (m_pSurface)
107
6.21k
        cairo_surface_destroy(m_pSurface);
108
6.21k
#endif
109
6.21k
}
110
111
void SvpSalFrame::GetFocus()
112
0
{
113
0
    if (m_nStyle == SalFrameStyleFlags::NONE)
114
0
        return;
115
0
    if( s_pFocusFrame == this )
116
0
        return;
117
    // FIXME: return if !m_bVisible
118
    // That's IMHO why CppunitTest_sd_tiledrendering crashes non-headless
119
120
0
    if( (m_nStyle & (SalFrameStyleFlags::OWNERDRAWDECORATION | SalFrameStyleFlags::FLOAT)) == SalFrameStyleFlags::NONE )
121
0
    {
122
0
        if( s_pFocusFrame )
123
0
            s_pFocusFrame->LoseFocus();
124
0
        s_pFocusFrame = this;
125
0
        m_rInstance.PostEvent(this, nullptr, SalEvent::GetFocus);
126
0
    }
127
0
}
128
129
void SvpSalFrame::LoseFocus()
130
0
{
131
0
    if( s_pFocusFrame == this )
132
0
    {
133
0
        m_rInstance.PostEvent(this, nullptr, SalEvent::LoseFocus);
134
0
        s_pFocusFrame = nullptr;
135
0
    }
136
0
}
137
138
basegfx::B2IVector SvpSalFrame::GetSurfaceFrameSize() const
139
126k
{
140
126k
    basegfx::B2IVector aFrameSize( maGeometry.width(), maGeometry.height() );
141
126k
    if( aFrameSize.getX() == 0 )
142
0
        aFrameSize.setX( 1 );
143
126k
    if( aFrameSize.getY() == 0 )
144
0
        aFrameSize.setY( 1 );
145
    // Creating backing surfaces for invisible windows costs a big chunk of RAM.
146
126k
    if (Application::IsHeadlessModeEnabled())
147
126k
         aFrameSize = basegfx::B2IVector( 1, 1 );
148
126k
    return aFrameSize;
149
126k
}
150
151
SalGraphics* SvpSalFrame::AcquireGraphics()
152
115k
{
153
115k
    SvpSalGraphics* pGraphics = new SvpSalGraphics();
154
115k
#ifndef IOS
155
115k
    pGraphics->setSurface(m_pSurface, GetSurfaceFrameSize());
156
115k
#endif
157
115k
    m_aGraphics.push_back( pGraphics );
158
115k
    return pGraphics;
159
115k
}
160
161
void SvpSalFrame::ReleaseGraphics( SalGraphics* pGraphics )
162
105k
{
163
105k
    SvpSalGraphics* pSvpGraphics = dynamic_cast<SvpSalGraphics*>(pGraphics);
164
105k
    std::erase(m_aGraphics, pSvpGraphics);
165
105k
    delete pSvpGraphics;
166
105k
}
167
168
bool SvpSalFrame::PostEvent(std::unique_ptr<ImplSVEvent> pData)
169
113k
{
170
113k
    m_rInstance.PostEvent(this, pData.release(), SalEvent::UserEvent);
171
113k
    return true;
172
113k
}
173
174
void SvpSalFrame::PostPaint() const
175
0
{
176
0
    if( m_bVisible )
177
0
    {
178
0
        SalPaintEvent aPEvt(0, 0, maGeometry.width(), maGeometry.height());
179
0
        aPEvt.mbImmediateUpdate = false;
180
0
        CallCallback( SalEvent::Paint, &aPEvt );
181
0
    }
182
0
}
183
184
void SvpSalFrame::SetTitle(const OUString& sTitle)
185
6.24k
{
186
6.24k
    m_sTitle = sTitle;
187
6.24k
}
188
189
void SvpSalFrame::SetIcon( sal_uInt16 )
190
0
{
191
0
}
192
193
void SvpSalFrame::SetMenu( SalMenu* )
194
0
{
195
0
}
196
197
void SvpSalFrame::SetExtendedFrameStyle( SalExtStyle )
198
12.6k
{
199
12.6k
}
200
201
void SvpSalFrame::Show( bool bVisible, bool bNoActivate )
202
0
{
203
0
    if (m_nStyle == SalFrameStyleFlags::NONE)
204
0
        return;
205
0
    if (bVisible == m_bVisible)
206
0
    {
207
0
        if (m_bVisible && !bNoActivate)
208
0
            GetFocus();
209
0
        return;
210
0
    }
211
212
0
    if (bVisible)
213
0
    {
214
0
        m_bVisible = true;
215
0
        m_rInstance.PostEvent(this, nullptr, SalEvent::Resize);
216
0
        if( ! bNoActivate )
217
0
            GetFocus();
218
0
    }
219
0
    else
220
0
    {
221
0
        m_bVisible = false;
222
0
        LoseFocus();
223
0
    }
224
0
}
225
226
void SvpSalFrame::SetMinClientSize( tools::Long nWidth, tools::Long nHeight )
227
0
{
228
0
    m_nMinWidth = nWidth;
229
0
    m_nMinHeight = nHeight;
230
0
}
231
232
void SvpSalFrame::SetMaxClientSize( tools::Long nWidth, tools::Long nHeight )
233
0
{
234
0
    m_nMaxWidth = nWidth;
235
0
    m_nMaxHeight = nHeight;
236
0
}
237
238
void SvpSalFrame::SetPosSize( tools::Long nX, tools::Long nY, tools::Long nWidth, tools::Long nHeight, sal_uInt16 nFlags )
239
11.0k
{
240
11.0k
    if( (nFlags & SAL_FRAME_POSSIZE_X) != 0 )
241
0
        maGeometry.setX(nX);
242
11.0k
    if( (nFlags & SAL_FRAME_POSSIZE_Y) != 0 )
243
0
        maGeometry.setY(nY);
244
11.0k
    if( (nFlags & SAL_FRAME_POSSIZE_WIDTH) != 0 )
245
11.0k
    {
246
11.0k
        maGeometry.setWidth(nWidth);
247
11.0k
        if (m_nMaxWidth > 0 && maGeometry.width() > m_nMaxWidth)
248
0
            maGeometry.setWidth(m_nMaxWidth);
249
11.0k
        if (m_nMinWidth > 0 && maGeometry.width() < m_nMinWidth)
250
0
            maGeometry.setWidth(m_nMinWidth);
251
11.0k
    }
252
11.0k
    if( (nFlags & SAL_FRAME_POSSIZE_HEIGHT) != 0 )
253
11.0k
    {
254
11.0k
        maGeometry.setHeight(nHeight);
255
11.0k
        if (m_nMaxHeight > 0 && maGeometry.height() > m_nMaxHeight)
256
0
            maGeometry.setHeight(m_nMaxHeight);
257
11.0k
        if (m_nMinHeight > 0 && maGeometry.height() < m_nMinHeight)
258
0
            maGeometry.setHeight(m_nMinHeight);
259
11.0k
    }
260
11.0k
#ifndef IOS
261
11.0k
    basegfx::B2IVector aFrameSize = GetSurfaceFrameSize();
262
11.0k
    if (!m_pSurface || cairo_image_surface_get_width(m_pSurface) != aFrameSize.getX() ||
263
0
                       cairo_image_surface_get_height(m_pSurface) != aFrameSize.getY() )
264
11.0k
    {
265
11.0k
        if (m_pSurface)
266
0
            cairo_surface_destroy(m_pSurface);
267
268
11.0k
        m_pSurface = cairo_image_surface_create(CAIRO_FORMAT_ARGB32,
269
11.0k
                                                aFrameSize.getX(),
270
11.0k
                                                aFrameSize.getY());
271
272
        // update device in existing graphics
273
11.0k
        for (auto const& graphic : m_aGraphics)
274
0
        {
275
0
             graphic->setSurface(m_pSurface, aFrameSize);
276
0
        }
277
11.0k
    }
278
11.0k
    if( m_bVisible )
279
0
        m_rInstance.PostEvent(this, nullptr, SalEvent::Resize);
280
11.0k
#endif
281
11.0k
}
282
283
11.0k
Size SvpSalFrame::GetClientSize() { return maGeometry.size(); }
284
285
void SvpSalFrame::GetWorkArea( AbsoluteScreenPixelRectangle& rRect )
286
0
{
287
0
    rRect = AbsoluteScreenPixelRectangle( AbsoluteScreenPixelPoint( 0, 0 ),
288
0
                       AbsoluteScreenPixelSize( VIRTUAL_DESKTOP_WIDTH, VIRTUAL_DESKTOP_HEIGHT ) );
289
0
}
290
291
SalFrame* SvpSalFrame::GetParent() const
292
0
{
293
0
    return m_pParent;
294
0
}
295
296
void SvpSalFrame::SetWindowState(const vcl::WindowData& rState)
297
0
{
298
    // Request for position or size change
299
0
    if (!(rState.mask() & vcl::WindowDataMask::PosSize))
300
0
        return;
301
302
0
    tools::Long nX = maGeometry.x();
303
0
    tools::Long nY = maGeometry.y();
304
0
    tools::Long nWidth = maGeometry.width();
305
0
    tools::Long nHeight = maGeometry.height();
306
307
    // change requested properties
308
0
    if (rState.mask() & vcl::WindowDataMask::X)
309
0
        nX = rState.x();
310
0
    if (rState.mask() & vcl::WindowDataMask::Y)
311
0
        nY = rState.y();
312
0
    if (rState.mask() & vcl::WindowDataMask::Width)
313
0
        nWidth = rState.width();
314
0
    if (rState.mask() & vcl::WindowDataMask::Height)
315
0
        nHeight = rState.height();
316
317
0
    SetPosSize( nX, nY, nWidth, nHeight,
318
0
                SAL_FRAME_POSSIZE_X | SAL_FRAME_POSSIZE_Y |
319
0
                SAL_FRAME_POSSIZE_WIDTH | SAL_FRAME_POSSIZE_HEIGHT );
320
0
}
321
322
vcl::WindowData SvpSalFrame::GetWindowState()
323
5.69k
{
324
5.69k
    vcl::WindowData aState;
325
5.69k
    aState.setPosSize(maGeometry.posSize());
326
5.69k
    aState.setState(vcl::WindowState::Normal);
327
5.69k
    aState.setMask(vcl::WindowDataMask::PosSizeState);
328
5.69k
    return aState;
329
5.69k
}
330
331
void SvpSalFrame::ShowFullScreen( bool, sal_Int32 )
332
0
{
333
0
    SetPosSize( 0, 0, VIRTUAL_DESKTOP_WIDTH, VIRTUAL_DESKTOP_HEIGHT,
334
0
                SAL_FRAME_POSSIZE_WIDTH | SAL_FRAME_POSSIZE_HEIGHT );
335
0
}
336
337
void SvpSalFrame::StartPresentation( bool )
338
0
{
339
0
}
340
341
void SvpSalFrame::SetAlwaysOnTop( bool )
342
0
{
343
0
}
344
345
void SvpSalFrame::ToTop(SalFrameToTop nFlags)
346
0
{
347
0
    if (m_nStyle == SalFrameStyleFlags::NONE)
348
0
        return;
349
0
    if (nFlags & SalFrameToTop::RestoreWhenMin)
350
0
        Show(true, false);
351
0
    else
352
0
        GetFocus();
353
0
}
354
355
void SvpSalFrame::SetPointer( PointerStyle )
356
0
{
357
0
}
358
359
void SvpSalFrame::CaptureMouse( bool )
360
0
{
361
0
}
362
363
void SvpSalFrame::SetPointerPos( tools::Long, tools::Long )
364
0
{
365
0
}
366
367
void SvpSalFrame::Flush()
368
0
{
369
0
}
370
371
void SvpSalFrame::SetInputContext( SalInputContext* )
372
0
{
373
0
}
374
375
void SvpSalFrame::EndExtTextInput( EndExtTextInputFlags )
376
0
{
377
0
}
378
379
OUString SvpSalFrame::GetKeyName(sal_uInt16 nKeyCode)
380
0
{
381
    // Skip key combinations with any modifiers that we don’t support
382
0
    if ((nKeyCode & KEY_MODIFIERS_MASK & ~(KEY_SHIFT | KEY_MOD1 | KEY_MOD2)) != 0)
383
0
        return OUString();
384
385
0
    OUStringBuffer aResult;
386
387
0
    if ((nKeyCode & KEY_MOD1) != 0)
388
0
        aResult.append("Ctrl+");
389
0
    if ((nKeyCode & KEY_MOD2) != 0)
390
0
        aResult.append("Alt+");
391
0
    if ((nKeyCode & KEY_SHIFT) != 0)
392
0
        aResult.append("Shift+");
393
394
0
    static const struct
395
0
    {
396
0
        sal_uInt16 code;
397
0
        const char* name;
398
0
    } keyMap[] = {
399
0
        { KEY_DOWN, "Down" },
400
0
        { KEY_UP, "Up" },
401
0
        { KEY_LEFT, "Left" },
402
0
        { KEY_RIGHT, "Right" },
403
0
        { KEY_HOME, "Home" },
404
0
        { KEY_END, "End" },
405
0
        { KEY_PAGEUP, "PgUp" },
406
0
        { KEY_PAGEDOWN, "PgDown" },
407
0
        { KEY_RETURN, "Ret" },
408
0
        { KEY_ESCAPE, "Esc" },
409
0
        { KEY_TAB, "Tab" },
410
0
        { KEY_BACKSPACE, "BkSpace" },
411
0
        { KEY_SPACE, "Space" },
412
0
        { KEY_DELETE, "Del" },
413
0
        { KEY_ADD, "+" },
414
0
        { KEY_SUBTRACT, "-" },
415
0
        { KEY_DIVIDE, "/" },
416
0
        { KEY_MULTIPLY, "*" },
417
0
        { KEY_POINT, "." },
418
0
        { KEY_COMMA, "," },
419
0
        { KEY_LESS, "<" },
420
0
        { KEY_GREATER, ">" },
421
0
        { KEY_EQUAL, "=" },
422
0
        { KEY_TILDE, "~" },
423
0
        { KEY_BRACKETLEFT, "[" },
424
0
        { KEY_BRACKETRIGHT, "]" },
425
0
        { KEY_SEMICOLON, ";" },
426
0
        { KEY_QUOTERIGHT, "'" },
427
0
        { KEY_RIGHTCURLYBRACKET, "}" },
428
0
        { KEY_NUMBERSIGN, "#" },
429
0
        { KEY_COLON, ":" },
430
0
    };
431
432
0
    sal_uInt16 nUnmodifiedCode = (nKeyCode & KEY_CODE_MASK);
433
434
0
    if (nUnmodifiedCode >= KEY_A && nUnmodifiedCode <= KEY_Z)
435
0
        aResult.append(char('A' + nUnmodifiedCode - KEY_A));
436
0
    else if (nUnmodifiedCode >= KEY_F1 && nUnmodifiedCode <= KEY_F26)
437
0
        aResult.append(OUStringChar('F') + OUString::number(nUnmodifiedCode - KEY_F1 + 1));
438
0
    else if (nUnmodifiedCode >= KEY_0 && nUnmodifiedCode <= KEY_9)
439
0
        aResult.append(char('0' + nUnmodifiedCode - KEY_0));
440
0
    else if (auto it = std::find_if(keyMap, keyMap + std::size(keyMap),
441
0
                                    [=](const auto& p) { return p.code == nUnmodifiedCode; });
442
0
             it != keyMap + std::size(keyMap))
443
0
        aResult.appendAscii(it->name);
444
0
    else
445
0
        return OUString();
446
447
0
    return aResult.makeStringAndClear();
448
0
}
449
450
bool SvpSalFrame::MapUnicodeToKeyCode( sal_Unicode, LanguageType, vcl::KeyCode& )
451
0
{
452
0
    return false;
453
0
}
454
455
LanguageType SvpSalFrame::GetInputLanguage()
456
0
{
457
0
    return LANGUAGE_DONTKNOW;
458
0
}
459
460
void SvpSalFrame::UpdateSettings( AllSettings& rSettings )
461
2
{
462
2
    StyleSettings aStyleSettings = rSettings.GetStyleSettings();
463
464
2
    Color aBackgroundColor( 0xef, 0xef, 0xef );
465
2
    aStyleSettings.BatchSetBackgrounds( aBackgroundColor, false );
466
2
    aStyleSettings.SetMenuColor( aBackgroundColor );
467
2
    aStyleSettings.SetMenuBarColor( aBackgroundColor );
468
469
2
    if (comphelper::LibreOfficeKit::isActive()) // TODO: remove this.
470
0
    {
471
0
        vcl::Font aStdFont( FAMILY_SWISS, Size( 0, 14 ) );
472
0
        aStdFont.SetCharSet( osl_getThreadTextEncoding() );
473
0
        aStdFont.SetWeight( WEIGHT_NORMAL );
474
0
        aStdFont.SetFamilyName( u"Liberation Sans"_ustr );
475
0
        aStyleSettings.BatchSetFonts( aStdFont, aStdFont );
476
477
0
        aStdFont.SetFontSize(Size(0, 12));
478
0
        aStyleSettings.SetMenuFont(aStdFont);
479
480
0
        SvpSalGraphics* pGraphics = m_aGraphics.empty() ? nullptr : m_aGraphics.back();
481
0
        bool bFreeGraphics = false;
482
0
        if (!pGraphics)
483
0
        {
484
0
            pGraphics = dynamic_cast<SvpSalGraphics*>(AcquireGraphics());
485
0
            if (!pGraphics)
486
0
            {
487
0
                SAL_WARN("vcl.gtk3", "Could not get graphics - unable to update settings");
488
0
                return;
489
0
            }
490
0
            bFreeGraphics = true;
491
0
        }
492
0
        rSettings.SetStyleSettings(aStyleSettings);
493
0
#ifndef IOS // For now...
494
0
        pGraphics->UpdateSettings(rSettings);
495
0
#endif
496
0
        if (bFreeGraphics)
497
0
            ReleaseGraphics(pGraphics);
498
0
    }
499
2
    else
500
2
        rSettings.SetStyleSettings(aStyleSettings);
501
2
}
502
503
void SvpSalFrame::Beep()
504
0
{
505
0
}
506
507
const SystemEnvData& SvpSalFrame::GetSystemData() const
508
0
{
509
0
    return m_aSystemChildData;
510
0
}
511
512
SalFrame::SalPointerState SvpSalFrame::GetPointerState()
513
0
{
514
0
    SalPointerState aState;
515
0
    aState.mnState = 0;
516
0
    return aState;
517
0
}
518
519
KeyIndicatorState SvpSalFrame::GetIndicatorState()
520
0
{
521
0
    return KeyIndicatorState::NONE;
522
0
}
523
524
void SvpSalFrame::SetParent( SalFrame* pNewParent )
525
0
{
526
0
    if( m_pParent )
527
0
        std::erase(m_pParent->m_aChildren, this);
528
0
    m_pParent = static_cast<SvpSalFrame*>(pNewParent);
529
0
}
530
531
void SvpSalFrame::SetPluginParent( SystemParentData* )
532
0
{
533
0
}
534
535
void SvpSalFrame::ResetClipRegion()
536
0
{
537
0
}
538
539
void SvpSalFrame::BeginSetClipRegion( sal_uInt32 )
540
0
{
541
0
}
542
543
void SvpSalFrame::UnionClipRegion( tools::Long, tools::Long, tools::Long, tools::Long )
544
0
{
545
0
}
546
547
void SvpSalFrame::EndSetClipRegion()
548
0
{
549
0
}
550
551
/* vim:set shiftwidth=4 softtabstop=4 expandtab: */