1
1
package kvm
2
2
3
3
import (
4
+ "errors"
4
5
"fmt"
5
6
"log"
7
+ "os"
8
+ "strconv"
6
9
"time"
7
10
)
8
11
9
12
var currentScreen = "ui_Boot_Screen"
13
+ var backlightState = 0 // 0 - NORMAL, 1 - DIMMED, 2 - OFF
14
+
15
+ var (
16
+ dimTicker * time.Ticker
17
+ offTicker * time.Ticker
18
+ )
19
+
20
+ const (
21
+ touchscreenDevice string = "/dev/input/event1"
22
+ backlightControlClass string = "/sys/class/backlight/backlight/brightness"
23
+ )
10
24
11
25
func switchToScreen (screen string ) {
12
26
_ , err := CallCtrlAction ("lv_scr_load" , map [string ]interface {}{"obj" : screen })
@@ -65,6 +79,7 @@ func requestDisplayUpdate() {
65
79
return
66
80
}
67
81
go func () {
82
+ wakeDisplay (false )
68
83
fmt .Println ("display updating........................" )
69
84
//TODO: only run once regardless how many pending updates
70
85
updateDisplay ()
@@ -83,6 +98,155 @@ func updateStaticContents() {
83
98
updateLabelIfChanged ("ui_Status_Content_Device_Id_Content_Label" , GetDeviceID ())
84
99
}
85
100
101
+ // setDisplayBrightness sets /sys/class/backlight/backlight/brightness to alter
102
+ // the backlight brightness of the JetKVM hardware's display.
103
+ func setDisplayBrightness (brightness int ) error {
104
+ // NOTE: The actual maximum value for this is 255, but out-of-the-box, the value is set to 64.
105
+ // The maximum set here is set to 100 to reduce the risk of drawing too much power (and besides, 255 is very bright!).
106
+ if brightness > 100 || brightness < 0 {
107
+ return errors .New ("brightness value out of bounds, must be between 0 and 100" )
108
+ }
109
+
110
+ // Check the display backlight class is available
111
+ if _ , err := os .Stat (backlightControlClass ); errors .Is (err , os .ErrNotExist ) {
112
+ return errors .New ("brightness value cannot be set, possibly not running on JetKVM hardware" )
113
+ }
114
+
115
+ // Set the value
116
+ bs := []byte (strconv .Itoa (brightness ))
117
+ err := os .WriteFile (backlightControlClass , bs , 0644 )
118
+ if err != nil {
119
+ return err
120
+ }
121
+
122
+ fmt .Printf ("display: set brightness to %v\n " , brightness )
123
+ return nil
124
+ }
125
+
126
+ // tick_displayDim() is called when when dim ticker expires, it simply reduces the brightness
127
+ // of the display by half of the max brightness.
128
+ func tick_displayDim () {
129
+ err := setDisplayBrightness (config .DisplayMaxBrightness / 2 )
130
+ if err != nil {
131
+ fmt .Printf ("display: failed to dim display: %s\n " , err )
132
+ }
133
+
134
+ dimTicker .Stop ()
135
+
136
+ backlightState = 1
137
+ }
138
+
139
+ // tick_displayOff() is called when the off ticker expires, it turns off the display
140
+ // by setting the brightness to zero.
141
+ func tick_displayOff () {
142
+ err := setDisplayBrightness (0 )
143
+ if err != nil {
144
+ fmt .Printf ("display: failed to turn off display: %s\n " , err )
145
+ }
146
+
147
+ offTicker .Stop ()
148
+
149
+ backlightState = 2
150
+ }
151
+
152
+ // wakeDisplay sets the display brightness back to config.DisplayMaxBrightness and stores the time the display
153
+ // last woke, ready for displayTimeoutTick to put the display back in the dim/off states.
154
+ // Set force to true to skip the backlight state check, this should be done if altering the tickers.
155
+ func wakeDisplay (force bool ) {
156
+ if backlightState == 0 && ! force {
157
+ return
158
+ }
159
+
160
+ // Don't try to wake up if the display is turned off.
161
+ if config .DisplayMaxBrightness == 0 {
162
+ return
163
+ }
164
+
165
+ err := setDisplayBrightness (config .DisplayMaxBrightness )
166
+ if err != nil {
167
+ fmt .Printf ("display wake failed, %s\n " , err )
168
+ }
169
+
170
+ if config .DisplayDimAfterSec != 0 {
171
+ dimTicker .Reset (time .Duration (config .DisplayDimAfterSec ) * time .Second )
172
+ }
173
+
174
+ if config .DisplayOffAfterSec != 0 {
175
+ offTicker .Reset (time .Duration (config .DisplayOffAfterSec ) * time .Second )
176
+ }
177
+ backlightState = 0
178
+ }
179
+
180
+ // watchTsEvents monitors the touchscreen for events and simply calls wakeDisplay() to ensure the
181
+ // touchscreen interface still works even with LCD dimming/off.
182
+ // TODO: This is quite a hack, really we should be getting an event from jetkvm_native, or the whole display backlight
183
+ // control should be hoisted up to jetkvm_native.
184
+ func watchTsEvents () {
185
+ ts , err := os .OpenFile (touchscreenDevice , os .O_RDONLY , 0666 )
186
+ if err != nil {
187
+ fmt .Printf ("display: failed to open touchscreen device: %s\n " , err )
188
+ return
189
+ }
190
+
191
+ defer ts .Close ()
192
+
193
+ // This buffer is set to 24 bytes as that's the normal size of events on /dev/input
194
+ // Reference: https://www.kernel.org/doc/Documentation/input/input.txt
195
+ // This could potentially be set higher, to require multiple events to wake the display.
196
+ buf := make ([]byte , 24 )
197
+ for {
198
+ _ , err := ts .Read (buf )
199
+ if err != nil {
200
+ fmt .Printf ("display: failed to read from touchscreen device: %s\n " , err )
201
+ return
202
+ }
203
+
204
+ wakeDisplay (false )
205
+ }
206
+ }
207
+
208
+ // startBacklightTickers starts the two tickers for dimming and switching off the display
209
+ // if they're not already set. This is done separately to the init routine as the "never dim"
210
+ // option has the value set to zero, but time.NewTicker only accept positive values.
211
+ func startBacklightTickers () {
212
+ // Don't start the tickers if the display is switched off.
213
+ // Set the display to off if that's the case.
214
+ if config .DisplayMaxBrightness == 0 {
215
+ setDisplayBrightness (0 )
216
+ return
217
+ }
218
+
219
+ if dimTicker == nil && config .DisplayDimAfterSec != 0 {
220
+ fmt .Printf ("display: dim_ticker has started\n " )
221
+ dimTicker = time .NewTicker (time .Duration (config .DisplayDimAfterSec ) * time .Second )
222
+ defer dimTicker .Stop ()
223
+
224
+ go func () {
225
+ for {
226
+ select {
227
+ case <- dimTicker .C :
228
+ tick_displayDim ()
229
+ }
230
+ }
231
+ }()
232
+ }
233
+
234
+ if offTicker == nil && config .DisplayOffAfterSec != 0 {
235
+ fmt .Printf ("display: off_ticker has started\n " )
236
+ offTicker = time .NewTicker (time .Duration (config .DisplayOffAfterSec ) * time .Second )
237
+ defer offTicker .Stop ()
238
+
239
+ go func () {
240
+ for {
241
+ select {
242
+ case <- offTicker .C :
243
+ tick_displayOff ()
244
+ }
245
+ }
246
+ }()
247
+ }
248
+ }
249
+
86
250
func init () {
87
251
go func () {
88
252
waitCtrlClientConnected ()
@@ -91,6 +255,10 @@ func init() {
91
255
updateStaticContents ()
92
256
displayInited = true
93
257
fmt .Println ("display inited" )
258
+ startBacklightTickers ()
259
+ wakeDisplay (true )
94
260
requestDisplayUpdate ()
95
261
}()
262
+
263
+ go watchTsEvents ()
96
264
}
0 commit comments