summaryrefslogtreecommitdiff
path: root/src/helpers/Monitor.hpp
blob: 59a64237ef06c42cfadc4e519fd5b2092723e3d2 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
#pragma once

#include "../defines.hpp"
#include <stack>
#include <vector>
#include "../SharedDefs.hpp"
#include "MiscFunctions.hpp"
#include "WLClasses.hpp"
#include <array>
#include "AnimatedVariable.hpp"
#include "CMType.hpp"

#include <xf86drmMode.h>
#include "MonitorZoomController.hpp"
#include "../render/Texture.hpp"
#include "../render/Framebuffer.hpp"
#include "MonitorResources.hpp"
#include "time/Timer.hpp"
#include "math/Math.hpp"
#include "../desktop/reserved/ReservedArea.hpp"
#include <optional>
#include "cm/ColorManagement.hpp"
#include "signal/Signal.hpp"
#include "DamageRing.hpp"
#include <aquamarine/output/Output.hpp>
#include <aquamarine/allocator/Swapchain.hpp>
#include <hyprutils/os/FileDescriptor.hpp>
#include <sys/types.h>

#include "../helpers/TransferFunction.hpp"
#include "../config/shared/monitor/MonitorRule.hpp"

class CMonitorFrameScheduler;
namespace Monitor {
    class CMonitorResources;
}

// Enum for the different types of auto directions, e.g. auto-left, auto-up.
enum eAutoDirs : uint8_t {
    DIR_AUTO_NONE = 0, /* None will be treated as right. */
    DIR_AUTO_UP,
    DIR_AUTO_DOWN,
    DIR_AUTO_LEFT,
    DIR_AUTO_RIGHT,
    DIR_AUTO_CENTER_UP,
    DIR_AUTO_CENTER_DOWN,
    DIR_AUTO_CENTER_LEFT,
    DIR_AUTO_CENTER_RIGHT
};

struct SMonitorRule {
    eAutoDirs              autoDir       = DIR_AUTO_NONE;
    std::string            name          = "";
    Vector2D               resolution    = Vector2D(1280, 720);
    Vector2D               offset        = Vector2D(0, 0);
    float                  scale         = 1;
    float                  refreshRate   = 60; // Hz
    bool                   disabled      = false;
    wl_output_transform    transform     = WL_OUTPUT_TRANSFORM_NORMAL;
    std::string            mirrorOf      = "";
    bool                   enable10bit   = false;
    NCMType::eCMType       cmType        = NCMType::CM_SRGB;
    NTransferFunction::eTF sdrEotf       = NTransferFunction::TF_DEFAULT;
    float                  sdrSaturation = 1.0f; // SDR -> HDR
    float                  sdrBrightness = 1.0f; // SDR -> HDR
    Desktop::CReservedArea reservedArea;
    std::string            iccFile;

    int                    supportsWideColor = 0;    // 0 - auto, 1 - force enable, -1 - force disable
    int                    supportsHDR       = 0;    // 0 - auto, 1 - force enable, -1 - force disable
    float                  sdrMinLuminance   = 0.2f; // SDR -> HDR
    int                    sdrMaxLuminance   = 80;   // SDR -> HDR

    // Incorrect values will result in reduced luminance range or incorrect tonemapping. Shouldn't damage the HW. Use with care in case of a faulty monitor firmware.
    float              minLuminance    = -1.0f; // >= 0 overrides EDID
    int                maxLuminance    = -1;    // >= 0 overrides EDID
    int                maxAvgLuminance = -1;    // >= 0 overrides EDID

    drmModeModeInfo    drmMode = {};
    std::optional<int> vrr;
};

class CMonitor;
class CSyncTimeline;
class CEventLoopTimer;

class CMonitorState {
  public:
    CMonitorState(CMonitor* owner);
    ~CMonitorState() = default;

    bool commit();
    bool test();
    bool updateSwapchain();
    void applyModeWithSwapchain(const SP<Aquamarine::SOutputMode>& mode);
    void applyCustomModeWithSwapchain(const SP<Aquamarine::SOutputMode>& mode);

  private:
    void      ensureBufferPresent();

    CMonitor* m_owner = nullptr;
};

class CMonitor {
  public:
    CMonitor(SP<Aquamarine::IOutput> output);
    ~CMonitor();

    Vector2D                    m_position         = Vector2D(-1, -1); // means unset
    Vector2D                    m_xwaylandPosition = Vector2D(-1, -1); // means unset
    Config::eAutoDirs           m_autoDir          = Config::DIR_AUTO_NONE;
    Vector2D                    m_size             = Vector2D(0, 0);
    Vector2D                    m_pixelSize        = Vector2D(0, 0);
    Vector2D                    m_transformedSize  = Vector2D(0, 0);

    MONITORID                   m_id                     = MONITOR_INVALID;
    PHLWORKSPACE                m_activeWorkspace        = nullptr;
    PHLWORKSPACE                m_activeSpecialWorkspace = nullptr;
    float                       m_setScale               = 1; // scale set by cfg
    float                       m_scale                  = 1; // real scale

    std::string                 m_name             = "";
    std::string                 m_description      = "";
    std::string                 m_shortDescription = "";

    drmModeModeInfo             m_customDrmMode = {};

    Desktop::CReservedArea      m_reservedArea;

    CMonitorState               m_state;
    CDamageRing                 m_damage;

    SP<Aquamarine::IOutput>     m_output;
    float                       m_refreshRate     = 60; // Hz
    int                         m_forceFullFrames = 0;
    bool                        m_scheduledRecalc = false;
    wl_output_transform         m_transform       = WL_OUTPUT_TRANSFORM_NORMAL;
    float                       m_xwaylandScale   = 1.f;

    std::optional<Vector2D>     m_forceSize;
    SP<Aquamarine::SOutputMode> m_currentMode;
    SP<Aquamarine::CSwapchain>  m_cursorSwapchain;
    uint32_t                    m_drmFormat     = DRM_FORMAT_INVALID;
    uint32_t                    m_prevDrmFormat = DRM_FORMAT_INVALID;

    CMonitorZoomController      m_zoomController;

    bool                        m_dpmsStatus       = true;
    bool                        m_vrrActive        = false; // this can be TRUE even if VRR is not active in the case that this display does not support it.
    bool                        m_enabled10bit     = false; // as above, this can be TRUE even if 10 bit failed.
    NCMType::eCMType            m_cmType           = NCMType::CM_SRGB;
    NTransferFunction::eTF      m_sdrEotf          = NTransferFunction::TF_DEFAULT;
    float                       m_sdrSaturation    = 1.0f;
    float                       m_sdrBrightness    = 1.0f;
    float                       m_sdrMinLuminance  = 0.2f;
    int                         m_sdrMaxLuminance  = 80;
    bool                        m_createdByUser    = false;
    bool                        m_isUnsafeFallback = false;

    SP<CEventLoopTimer>         m_dpmsRetryTimer;

    bool                        m_pendingFrame    = false; // if we schedule a frame during rendering, reschedule it after
    bool                        m_renderingActive = false;

    bool                        m_ratsScheduled = false;
    CTimer                      m_lastPresentationTimer;

    bool                        m_isBeingLeased = false;

    Config::CMonitorRule        m_activeMonitorRule;

    SP<Render::ITexture>        m_splash;
    SP<Render::ITexture>        m_background;

    // explicit sync
    Hyprutils::OS::CFileDescriptor m_inFence; // TODO: remove when aq uses CFileDescriptor

    PHLMONITORREF                  m_self;

    UP<CMonitorFrameScheduler>     m_frameScheduler;

    // mirroring
    PHLMONITORREF              m_mirrorOf;
    std::vector<PHLMONITORREF> m_mirrors;

    // ctm
    Mat3x3 m_ctm        = Mat3x3::identity();
    bool   m_ctmUpdated = false;

    // for tearing
    PHLWINDOWREF m_solitaryClient;

    // for direct scanout
    PHLWINDOWREF m_lastScanout;
    bool         m_directScanoutIsActive    = false; // for cleanup logic. m_lastScanout.expired() can become true before the DS cleanup if client crashes/exits while DS is active.
    bool         m_scanoutNeedsCursorUpdate = false;

    // for special fade/blur
    PHLANIMVAR<float> m_specialFade;

    // for dpms off anim
    PHLANIMVAR<float> m_dpmsBlackOpacity;
    bool              m_pendingDpmsAnimation        = false;
    int               m_pendingDpmsAnimationCounter = 0;

    PHLANIMVAR<float> m_cursorZoom;

    // for fading in the wallpaper because it doesn't happen instantly (it's loaded async)
    PHLANIMVAR<float> m_backgroundOpacity;

    // for initial zoom anim
    PHLANIMVAR<float> m_zoomAnimProgress;
    CTimer            m_newMonitorAnimTimer;
    int               m_zoomAnimFrameCounter = 0;

    struct {
        bool canTear         = false;
        bool nextRenderTorn  = false;
        bool activelyTearing = false;

        bool busy                    = false;
        bool frameScheduledWhileBusy = false;
    } m_tearingState;

    struct {
        CSignalT<> commit;
        CSignalT<> destroy;
        CSignalT<> connect;
        CSignalT<> disconnect;
        CSignalT<> dpmsChanged;
        CSignalT<> modeChanged;
        CSignalT<> presented;
    } m_events;

    std::array<std::vector<PHLLSREF>, 4> m_layerSurfaceLayers;

    // keep in sync with HyprCtl
    enum eDSBlockReason : uint16_t {
        DS_OK = 0,

        DS_BLOCK_UNKNOWN   = (1 << 0),
        DS_BLOCK_USER      = (1 << 1),
        DS_BLOCK_WINDOWED  = (1 << 2),
        DS_BLOCK_CONTENT   = (1 << 3),
        DS_BLOCK_MIRROR    = (1 << 4),
        DS_BLOCK_RECORD    = (1 << 5),
        DS_BLOCK_SW        = (1 << 6),
        DS_BLOCK_CANDIDATE = (1 << 7),
        DS_BLOCK_SURFACE   = (1 << 8),
        DS_BLOCK_TRANSFORM = (1 << 9),
        DS_BLOCK_DMA       = (1 << 10),
        DS_BLOCK_FAILED    = (1 << 11),
        DS_BLOCK_CM        = (1 << 12),

        DS_CHECKS_COUNT = 14,
    };

    // keep in sync with HyprCtl
    enum eSolitaryCheck : uint32_t {
        SC_OK = 0,

        SC_UNKNOWN      = (1 << 0),
        SC_NOTIFICATION = (1 << 1),
        SC_LOCK         = (1 << 2),
        SC_WORKSPACE    = (1 << 3),
        SC_WINDOWED     = (1 << 4),
        SC_DND          = (1 << 5),
        SC_SPECIAL      = (1 << 6),
        SC_ALPHA        = (1 << 7),
        SC_OFFSET       = (1 << 8),
        SC_CANDIDATE    = (1 << 9),
        SC_OPAQUE       = (1 << 10),
        SC_TRANSFORM    = (1 << 11),
        SC_OVERLAYS     = (1 << 12),
        SC_FLOAT        = (1 << 13),
        SC_WORKSPACES   = (1 << 14),
        SC_SURFACES     = (1 << 15),
        SC_ERRORBAR     = (1 << 16),

        SC_CHECKS_COUNT = 17,
    };

    // keep in sync with HyprCtl
    enum eTearingCheck : uint8_t {
        TC_OK = 0,

        TC_UNKNOWN   = (1 << 0),
        TC_NOT_TORN  = (1 << 1),
        TC_USER      = (1 << 2),
        TC_ZOOM      = (1 << 3),
        TC_SUPPORT   = (1 << 4),
        TC_CANDIDATE = (1 << 5),
        TC_WINDOW    = (1 << 6),
        TC_HW_CURSOR = (1 << 7),

        TC_CHECKS_COUNT = 8,
    };

    // methods
    void        onConnect(bool noRule);
    void        onDisconnect(bool destroy = false);
    void        applyCMType(NCMType::eCMType cmType, NTransferFunction::eTF cmSdrEotf);
    bool        applyMonitorRule(Config::CMonitorRule&& pMonitorRule, bool force = false);
    void        addDamage(const pixman_region32_t* rg);
    void        addDamage(const CRegion& rg);
    void        addDamage(const CBox& box);
    bool        shouldSkipScheduleFrameOnMouseEvent();
    void        setMirror(const std::string&);
    bool        isMirror();
    bool        matchesStaticSelector(const std::string& selector) const;
    float       getDefaultScale();
    void        changeWorkspace(const PHLWORKSPACE& pWorkspace, bool internal = false, bool noMouseMove = false, bool noFocus = false);
    void        changeWorkspace(const WORKSPACEID& id, bool internal = false, bool noMouseMove = false, bool noFocus = false);
    void        setSpecialWorkspace(const PHLWORKSPACE& pWorkspace);
    void        setSpecialWorkspace(const WORKSPACEID& id);
    void        moveTo(const Vector2D& pos);
    Vector2D    middle();
    WORKSPACEID activeWorkspaceID();
    WORKSPACEID activeSpecialWorkspaceID();
    CBox        logicalBox();
    CBox        logicalBoxMinusReserved();
    void        scheduleDone();
    uint32_t    isSolitaryBlocked(bool full = false);
    void        recheckSolitary();
    uint8_t     isTearingBlocked(bool full = false);
    bool        updateTearing();
    uint16_t    isDSBlocked(bool full = false);
    bool        attemptDirectScanout();
    bool        canAttemptDirectScanoutFast() const;
    bool        isMultiGPU();
    void        setCTM(const Mat3x3& ctm);
    void        onCursorMovedOnMonitor();
    void        setDPMS(bool on);
    bool        shouldUseSoftwareCursors();

    //
    const Mat3x3& getTransformMatrix();
    const Mat3x3& getScaleMatrix();

    void          debugLastPresentation(const std::string& message);

    bool          supportsWideColor();
    bool          supportsHDR();
    float         minLuminance(float defaultValue = 0);
    int           maxLuminance(int defaultValue = 80);
    int           maxAvgLuminance(int defaultValue = 80);
    float         maxFALL();
    float         maxCLL();

    bool          wantsWideColor();
    bool          wantsHDR();

    bool          inHDR();
    bool          gammaRampsInUse();

    /// Has an active workspace with a real fullscreen window (includes special workspace)
    bool inFullscreenMode();
    /// Get fullscreen window from active or special workspace
    PHLWINDOW                                                   getFullscreenWindow();
    std::optional<NColorManagement::PImageDescription>          getFSImageDescription();

    NColorManagement::SPCPRimaries                              getMasteringPrimaries();
    NColorManagement::SImageDescription::SPCMasteringLuminances getMasteringLuminances();

    uint32_t                                                    getPreferredReadFormat();

    bool                                                        needsCM();
    /// Can do CM without shader (forDSmode ? check output image description : check workbuffer image description)
    bool                                canNoShaderCM(bool forDSmode = false);
    bool                                doesNoShaderCM();

    bool                                m_enabled             = false;
    bool                                m_renderingInitPassed = false;

    PHLWINDOWREF                        m_previousFSWindow;
    bool                                m_needsHDRupdate = false;

    std::optional<dev_t>                m_cachedAllocatorDRMDev;
    std::optional<dev_t>                m_cachedCompositorDRMDev;
    int                                 m_cachedAllocatorDRMFD  = -1;
    int                                 m_cachedCompositorDRMFD = -1;
    std::optional<bool>                 m_cachedSameGPU;

    NColorManagement::PImageDescription m_imageDescription = NColorManagement::CImageDescription::from(NColorManagement::SImageDescription{});
    bool                                m_noShaderCTM      = false; // sets drm CTM, restore needed

    bool                                m_blurFBDirty        = true;
    bool                                m_blurFBShouldRender = false;

    // For the list lookup

    bool operator==(const CMonitor& rhs) {
        return m_position == rhs.m_position && m_size == rhs.m_size && m_name == rhs.m_name;
    }

    bool                           needsACopyFB();
    bool                           needsUnmodifiedCopy();
    bool                           useFP16();
    WP<Monitor::CMonitorResources> resources();

  private:
    void                    updateMatrix();
    Mat3x3                  m_projMatrix;
    Mat3x3                  m_projOutputMatrix;

    void                    setupDefaultWS(const Config::CMonitorRule&);
    WORKSPACEID             findAvailableDefaultWS();
    void                    commitDPMSState(bool state);
    void                    updateVCGTRamps();

    bool                    m_doneScheduled = false;
    bool                    m_vcgtRampsSet  = false;
    std::stack<WORKSPACEID> m_prevWorkSpaces;

    // Resources
    UP<Monitor::CMonitorResources> m_resources;

    struct {
        CHyprSignalListener frame;
        CHyprSignalListener destroy;
        CHyprSignalListener state;
        CHyprSignalListener needsFrame;
        CHyprSignalListener presented;
        CHyprSignalListener commit;
    } m_listeners;

    int   m_supportsWideColor = 0;
    int   m_supportsHDR       = 0;
    float m_minLuminance      = -1.0f;
    int   m_maxLuminance      = -1;
    int   m_maxAvgLuminance   = -1;
};