summaryrefslogtreecommitdiff
path: root/src/helpers/Monitor.hpp
blob: d1f9a5560bfbc05c36ca5ac847916f82ae3d11dd (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
#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 "time/Timer.hpp"
#include "math/Math.hpp"
#include "../desktop/reserved/ReservedArea.hpp"
#include <optional>
#include "../protocols/types/ColorManagement.hpp"
#include "signal/Signal.hpp"
#include "DamageRing.hpp"
#include <aquamarine/output/Output.hpp>
#include <aquamarine/allocator/Swapchain.hpp>
#include <hyprutils/os/FileDescriptor.hpp>

class CMonitorFrameScheduler;

// 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;
    int                    sdrEotf       = 0;
    float                  sdrSaturation = 1.0f; // SDR -> HDR
    float                  sdrBrightness = 1.0f; // SDR -> HDR
    Desktop::CReservedArea reservedArea;

    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 CEGLSync;
class CEventLoopTimer;

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

    bool commit();
    bool test();
    bool updateSwapchain();

  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
    eAutoDirs                   m_autoDir          = 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;
    Mat3x3                      m_projMatrix;
    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;
    int                         m_sdrEotf          = 0;
    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;

    SMonitorRule                m_activeMonitorRule;

    // 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<> 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_TEARING   = (1 << 11),
        DS_BLOCK_FAILED    = (1 << 12),
        DS_BLOCK_CM        = (1 << 13),

        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_CHECKS_COUNT = 7,
    };

    // methods
    void        onConnect(bool noRule);
    void        onDisconnect(bool destroy = false);
    void        applyCMType(NCMType::eCMType cmType, int cmSdrEotf);
    bool        applyMonitorRule(SMonitorRule* 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();
    void        updateMatrix();
    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();
    void        setCTM(const Mat3x3& ctm);
    void        onCursorMovedOnMonitor();
    void        setDPMS(bool on);

    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();

    /// 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();

    bool                                                        needsCM();
    /// Can do CM without shader
    bool                                canNoShaderCM();
    bool                                doesNoShaderCM();

    bool                                m_enabled             = false;
    bool                                m_renderingInitPassed = false;

    PHLWINDOWREF                        m_previousFSWindow;
    bool                                m_needsHDRupdate = false;

    NColorManagement::PImageDescription m_imageDescription;
    bool                                m_noShaderCTM = false; // sets drm CTM, restore needed

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

  private:
    void                    setupDefaultWS(const SMonitorRule&);
    WORKSPACEID             findAvailableDefaultWS();
    void                    commitDPMSState(bool state);

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

    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;
};