Repository navigation
Expand file tree
/
Copy pathPrompterService.qml
More file actions
150 lines (147 loc) · 5.11 KB
/
Copy pathPrompterService.qml
File metadata and controls
150 lines (147 loc) · 5.11 KB
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
import QtQuick
import Quickshell
import Quickshell.Io
import Quickshell.Wayland
import qs.Commons
// One engine for the whole shell, independent of how many bars are visible.
Item {
id: root
property var shell: null
readonly property string cli: String(Qt.resolvedUrl("bin/elgatoctl")).replace("file://", "")
property string screenName: ""
property string sourceName: ""
property string scriptName: ""
property string scriptText: ""
property int speed: 75
property int fontSize: 42
property bool flipped: false
property bool running: false
property bool mirroring: false
property bool mirroringWindow: false
property var windowSource: null
property bool playing: false
property int chapter: 0
property string lastError: ""
readonly property var chapters: {
var blocks = scriptText.split(/\n(?=#{1,2} )/)
return blocks.map(function(block) {
var match = block.match(/^#{1,2} ([^\n]*)\n?([\s\S]*)$/)
return match ? { title: match[1], body: match[2] } : { title: "", body: block }
}).filter(function(block) { return block.title || block.body.trim() })
}
function screenFor(name) {
var screens = Quickshell.screens || []
for (var i = 0; i < screens.length; i++) if (String(screens[i].name) === name) return screens[i]
return null
}
function configure(data) {
if (!data || running || mirroring) return
screenName = String(data.screen || "")
sourceName = String(data.source || "")
scriptName = String(data.script || "")
scriptText = String(data.text || "")
speed = Number(data.speed || 75)
fontSize = Number(data.font_size || 42)
flipped = !!data.flip
chapter = 0
}
function reload() { if (!loadProcess.running) loadProcess.running = true }
function start() {
lastError = ""
if (mirroring) stop()
if (!screenFor(screenName)) { lastError = "screen"; return }
if (!scriptText.trim()) { lastError = "script"; return }
chapter = 0
running = true
playing = false
}
function startMirror() {
lastError = ""
if (!screenFor(screenName)) { lastError = "screen"; return }
if (!screenFor(sourceName) || sourceName === screenName) { lastError = "mirror_source"; return }
if (running || mirroring) stop()
mirroringWindow = false
windowSource = null
mirroring = true
}
function startWindowMirror(source) {
lastError = ""
if (!screenFor(screenName)) { lastError = "screen"; return }
if (!source) { lastError = "window_source"; return }
if (running || mirroring) stop()
windowSource = source
mirroringWindow = true
mirroring = true
}
function stop() {
playing = false
running = false
mirroring = false
mirroringWindow = false
windowSource = null
}
function setFlip(value) {
flipped = value
}
function playPause() {
if (!running) start()
if (running) playing = !playing
}
function jump(delta) { chapter = Math.max(0, Math.min(chapters.length - 1, chapter + delta)) }
Process {
id: loadProcess
command: [root.cli, "prompter-state"]
stdout: StdioCollector {
waitForEnd: true
onStreamFinished: {
try { root.configure(JSON.parse(text)) }
catch (error) { root.lastError = "state" }
}
}
}
Component.onCompleted: reload()
Timer {
interval: 2000
repeat: true
running: root.running || root.mirroring
onTriggered: if (!root.screenFor(root.screenName)) root.stop()
}
Connections {
target: root.windowSource
function onClosed() {
if (root.mirroringWindow) {
root.stop()
root.lastError = "window_closed"
}
}
}
IpcHandler {
target: "omagato-prompter"
function start(): void { root.start() }
function mirror(): void { root.startMirror() }
function mirrorWindow(appId: string): void {
var windows = ToplevelManager.toplevels.values
var source = windows.find(function(item) { return item && item.appId === appId })
root.startWindowMirror(source || null)
}
function stop(): void { root.stop() }
function play(): void { if (!root.running) root.start(); if (root.running) root.playing = true }
function pause(): void { root.playing = false }
function playPause(): void { root.playPause() }
function faster(): void { root.speed = Math.min(300, root.speed + 10) }
function slower(): void { root.speed = Math.max(10, root.speed - 10) }
function nextChapter(): void { root.jump(1) }
function prevChapter(): void { root.jump(-1) }
function flip(): void { root.setFlip(!root.flipped) }
function reload(): void { root.reload() }
function status(): string {
return JSON.stringify({ running: root.running, mirroring: root.mirroring,
mirrorMode: root.mirroring ? (root.mirroringWindow ? "window" : "display") : "",
window: root.windowSource ? root.windowSource.title : "", captureReady: root.mirroring && overlay.captureReady,
playing: root.playing,
screen: root.screenName, source: root.sourceName, script: root.scriptName, speed: root.speed,
chapter: root.chapter, flip: root.flipped, error: root.lastError })
}
}
TeleprompterOverlay { id: overlay; service: root }
}