1 //===========================================
2 // Lumina-desktop source code
3 // Copyright (c) 2017, Ken Moore
4 // Available under the 3-clause BSD license
5 // See the LICENSE file for full details
6 //===========================================
7 #include "RootDesktopObject.h"
8 #include <QQmlEngine>
9 #include <QApplication>
10 #include <QScreen>
11 #include <global-objects.h>
12 #include <QDebug>
13
14 // === PUBLIC ===
RootDesktopObject(QObject * parent)15 RootDesktopObject::RootDesktopObject(QObject *parent) : QObject(parent){
16 last_window_up = 0;
17 //updateScreens(); //make sure the internal list is updated right away
18 connect(this, SIGNAL(changePanels(QStringList)), this, SLOT(setPanels(QStringList)) );
19 currentTimeTimer = new QTimer(this);
20 connect(currentTimeTimer, SIGNAL(timeout()), this, SLOT(updateCurrentTime()) );
21 availgeomTimer = new QTimer(this);
22 availgeomTimer->setInterval(100);
23 availgeomTimer->setSingleShot(true);
24 connect(availgeomTimer, SIGNAL(timeout()), this, SLOT(submitSessionGeom()) );
25 }
26
~RootDesktopObject()27 RootDesktopObject::~RootDesktopObject(){
28
29 }
30
RegisterType()31 void RootDesktopObject::RegisterType(){
32 static bool done = false;
33 if(done){ return; }
34 done=true;
35 qmlRegisterType<RootDesktopObject>("Lumina.Backend.RootDesktopObject", 2, 0, "RootDesktopObject");
36 //Also register any types that are needed by this class
37 ScreenObject::RegisterType();
38 NativeWindowObject::RegisterType();
39 OSInterface::RegisterType();
40 }
41
instance()42 RootDesktopObject* RootDesktopObject::instance(){
43 static RootDesktopObject* r_obj = new RootDesktopObject();
44 return r_obj;
45 }
46
47 //QML Read Functions
screens()48 QStringList RootDesktopObject::screens(){
49 if(s_objects.length()<1){ updateScreens(); }
50 //qDebug() << "Request Screens:" << s_objects.length();
51 QStringList names;
52 for(int i=0; i<s_objects.length(); i++){ names << s_objects[i]->name(); }
53 return names;
54 }
55
screen(QString id)56 ScreenObject* RootDesktopObject::screen(QString id){
57 if(s_objects.length()<1){ updateScreens(); }
58 //qDebug() << "Got Screen Request:" << id;
59 for(int i=0; i<s_objects.length(); i++){
60 if(s_objects[i]->name()==id){ return s_objects[i]; }
61 }
62 return 0;
63 }
64
panels()65 QStringList RootDesktopObject::panels(){
66 //qDebug() << "Request Panels:" << panel_objects.length();
67 QStringList names;
68 for(int i=0; i<panel_objects.length(); i++){ names << panel_objects[i]->name(); }
69 return names;
70 }
71
panel(QString id)72 PanelObject* RootDesktopObject::panel(QString id){
73 //qDebug() << "Got Panel Request:" << id;
74 for(int i=0; i<panel_objects.length(); i++){
75 if(panel_objects[i]->name()==id){ return panel_objects[i]; }
76 }
77 return 0;
78 }
79
windows()80 QStringList RootDesktopObject::windows(){
81 //qDebug() << "Request Panels:" << panel_objects.length();
82 QStringList names;
83 for(int i=0; i<window_objects.length(); i++){ names << QString::number(window_objects[i]->id()); }
84 return names;
85 }
86
window(QString id)87 NativeWindowObject* RootDesktopObject::window(QString id){
88 //qDebug() << "Got Panel Request:" << id;
89 WId chk = id.toInt(); //numerical ID's in this case
90 for(int i=0; i<window_objects.length(); i++){
91 if(window_objects[i]->id()==chk){ return window_objects[i]; }
92 }
93 return 0;
94 }
95
trayWindows()96 QStringList RootDesktopObject::trayWindows(){
97 //qDebug() << "Request Panels:" << panel_objects.length();
98 QStringList names;
99 for(int i=0; i<tray_window_objects.length(); i++){ names << QString::number(tray_window_objects[i]->id()); }
100 return names;
101 }
102
trayWindow(QString id)103 NativeWindowObject* RootDesktopObject::trayWindow(QString id){
104 //qDebug() << "Got Panel Request:" << id;
105 WId chk = id.toInt(); //numerical ID's in this case
106 for(int i=0; i<tray_window_objects.length(); i++){
107 if(tray_window_objects[i]->id()==chk){ return tray_window_objects[i]; }
108 }
109 return 0;
110 }
111
hasTrayWindows()112 bool RootDesktopObject::hasTrayWindows(){
113 return !tray_window_objects.isEmpty();
114 }
115
currentTime()116 QString RootDesktopObject::currentTime(){
117 return currentTimeString;
118 }
119
currentDateTime()120 QDateTime RootDesktopObject::currentDateTime(){
121 return currentDateTimeStruct;
122 }
123
os_interface()124 OSInterface* RootDesktopObject::os_interface(){
125 return OSInterface::instance();
126 }
127
setPanels(QList<PanelObject * > list)128 void RootDesktopObject::setPanels(QList<PanelObject*> list){
129 panel_objects = list;
130 emit panelsChanged();
131 }
132
setPanels(QStringList ids)133 void RootDesktopObject::setPanels(QStringList ids){
134 //Make this thread-safe for object creation
135 if(this->thread() != QThread::currentThread()){
136 //use internal signal/slot combo to change threads
137 this->emit changePanels(ids);
138 return;
139 }
140 //qDebug() << "GOT PANEL CHANGE:" << ids;
141 //Get the current bounding rectangle for the session
142 QRect total;
143 bool change = false;
144 for(int i=0; i<=s_objects.length(); i++){
145 QRect geom;
146 QString prefix;
147 if(i==s_objects.length()){
148 geom = total; //session geometry
149 prefix="session/";
150 }else{
151 geom = s_objects[i]->geometry();
152 total = total.united(geom);
153 prefix=s_objects[i]->name()+"/";
154 }
155 QStringList newids = ids.filter(prefix);
156 //qDebug() << " Check Panel IDs:" << prefix << newids << ids;
157 //First update/remove any current panel objects
158 for(int i=0; i<panel_objects.length(); i++){
159 if(newids.contains(panel_objects[i]->name()) ){
160 //qDebug() << " - Update Existing Panel:" << panel_objects[i]->name();
161 newids.removeAll(panel_objects[i]->name()); //already handled
162 panel_objects[i]->syncWithSettings(geom);
163 }else if(panel_objects[i]->name().startsWith(prefix) ){
164 //qDebug() << " - Remove Existing Panel:" << panel_objects[i]->name();
165 panel_objects.takeAt(i)->deleteLater();
166 i--;
167 change = true; //list changed
168 }
169 }
170 //Now create any new panel objects as needed
171 for(int i=0; i<newids.length(); i++){
172 //qDebug() << " - Create Panel:" << newids[i];
173 PanelObject *tmp = new PanelObject(newids[i], this);
174 tmp->syncWithSettings(geom);
175 panel_objects << tmp;
176 change = true; //list changed
177 }
178 } //end loop over screens+session
179 //Now calculate the available session geometry
180 QRegion sess(total);
181 for(int i=0; i<panel_objects.length(); i++){
182 sess = sess.subtracted( QRegion(panel_objects[i]->geometry()) );
183 }
184 if(sess != session_avail_geom){
185 session_avail_geom = sess;
186 emit sessionGeomAvailableChanged();
187 }
188
189 if(change){ emit panelsChanged(); }
190 }
191
setWindows(QList<NativeWindowObject * > list)192 void RootDesktopObject::setWindows(QList<NativeWindowObject*> list){
193 window_objects = list;
194 emit windowsChanged();
195 mousePositionChanged(true);
196 }
197
setTrayWindows(QList<NativeWindowObject * > list)198 void RootDesktopObject::setTrayWindows(QList<NativeWindowObject*> list){
199 tray_window_objects = list;
200 emit trayWindowsChanged();
201 mousePositionChanged(true);
202 }
203
updateCurrentTimeFormat(QString fmt)204 void RootDesktopObject::updateCurrentTimeFormat(QString fmt){
205 //sanitize the time format as needed
206 if(fmt.contains("z")){ fmt.replace("z",""); } //do not allow millisecond updates - too fast for the desktop
207 fmt = fmt.simplified();
208 //Verify that anything has changed first
209 if(currentTimeFormat == fmt && currentTimeTimer->isActive()){ return; } //nothing changed
210 if(currentTimeTimer->isActive()){ currentTimeTimer->stop(); } //make sure this does not trigger during the changeover
211 currentTimeFormat = fmt;
212 int interval = 1000; //default to 1 second intervals
213 //Adjust the refresh time for the clock based on the smallest unit requested
214 if(fmt.contains("s")){ interval=500; } //1/2 second pings for 1-second displays
215 else if(fmt.contains("m") || currentTimeFormat.isEmpty()){ interval = 5000; } //5 second pings for 1-minute displays
216 else if(fmt.contains("h")){ interval = 30000; } //30 second pings for 1-hour displays
217 currentTimeTimer->setInterval(interval);
218 updateCurrentTime(); //refresh the currently-available time
219 QTimer::singleShot(0,currentTimeTimer, SLOT(start()) );//start the update timer
220 }
221
logout()222 void RootDesktopObject::logout(){
223 //Emit the logout signal in a few ms (let the display close/sync first)
224 QTimer::singleShot(50, this, SIGNAL(startLogout()));
225 }
226
lockscreen()227 void RootDesktopObject::lockscreen(){
228 emit lockScreen();
229 }
230
mousePositionChanged(bool lowerall)231 void RootDesktopObject::mousePositionChanged(bool lowerall){
232 emit mouseMoved();
233 // Go through the transparent windows (in order of high->low in stack)
234 // and raise/lower the transparent overlays as needed
235 QPoint pos = QCursor::pos();
236 for(int i=window_objects.length()-1; i>=0; i--){
237 if(window_objects[i]->geometry().contains(pos) ){
238 if(last_window_up!= window_objects[i]){
239 if(last_window_up!=0){ Lumina::NWS->lowerWindow(last_window_up); }
240 Lumina::NWS->raiseWindow(window_objects[i]);
241 last_window_up = window_objects[i];
242 }
243 if(!lowerall){ return; } //found the currently-hovered window
244 }else if(lowerall){
245 Lumina::NWS->lowerWindow(window_objects[i]);
246 }
247 }
248 //failover for when no window has the mouse over it (lower all of them)
249 if(last_window_up!=0 && !lowerall){ Lumina::NWS->lowerWindow(last_window_up); }
250 }
251
launchApp(QString appOrPath)252 void RootDesktopObject::launchApp(QString appOrPath){
253 emit launchApplication(appOrPath);
254 }
255
256 //C++ Access Functions (simplifications for the QML ones)
windowObjects()257 QList<NativeWindowObject*> RootDesktopObject::windowObjects(){
258 return window_objects;
259
260 }
trayWindowObjects()261 QList<NativeWindowObject*> RootDesktopObject::trayWindowObjects(){
262 return tray_window_objects;
263 }
264
screenObjects()265 QList<ScreenObject*> RootDesktopObject::screenObjects(){
266 return s_objects;
267 }
268
availableGeometry()269 QRegion* RootDesktopObject::availableGeometry(){
270 return &session_avail_geom;
271 }
272
273 // === PUBLIC SLOTS ===
updateScreens()274 void RootDesktopObject::updateScreens(){
275 QList<QScreen*> scrns = QApplication::screens();
276 QList<ScreenObject*> tmp; //copy of the internal array initially
277 for(int i=0; i<scrns.length(); i++){
278 bool found = false;
279 for(int j=0; j<s_objects.length() && !found; j++){
280 if(s_objects[j]->name()==scrns[i]->name()){ found = true; tmp << s_objects.takeAt(j); }
281 }
282 if(!found){
283 //Create new screen object
284 tmp << new ScreenObject(scrns[i], this);
285 connect(tmp.last(), SIGNAL(availableGeomChanged()), this, SLOT(availableScreenGeomChanged()) );
286 }
287 }
288 //Delete any leftover objects
289 for(int i=0; i<s_objects.length(); i++){ s_objects[i]->deleteLater(); }
290 s_objects = tmp;
291 emit screensChanged();
292 for(int i=0; i<s_objects.length(); i++){
293 s_objects[i]->emit geomChanged();
294 }
295 }
296
ChangeWallpaper(QString screen,QString value)297 void RootDesktopObject::ChangeWallpaper(QString screen, QString value){
298 for(int i=0; i<s_objects.length(); i++){
299 if(s_objects[i]->name()==screen){ s_objects[i]->setBackground(value); break; }
300 }
301 }
302
CurrentWallpaper(QString screen)303 QString RootDesktopObject::CurrentWallpaper(QString screen){
304 for(int i=0; i<s_objects.length(); i++){
305 if(s_objects[i]->name()==screen){ return s_objects[i]->background();}
306 }
307 return ""; //unknown
308 }
309
310
311
312 // === PRIVATE ===
313
314 // === PRIVATE SLOTS ===
updateCurrentTime()315 void RootDesktopObject::updateCurrentTime(){
316 QDateTime DT = QDateTime::currentDateTime();
317 QString tmp;
318 if(currentTimeFormat.isEmpty()){ tmp = DT.toString(Qt::DefaultLocaleShortDate); }
319 else{ tmp = DT.toString(currentTimeFormat); }
320 if(tmp!=currentTimeString){ //prevent sending signals to update the interface if nothing changed
321 currentDateTimeStruct = DT;
322 currentTimeString = tmp;
323 emit currentTimeChanged();
324 //qDebug() << "Current Time Changed:" << currentTimeString;
325 }
326 }
327
availableScreenGeomChanged()328 void RootDesktopObject::availableScreenGeomChanged(){
329 if(availgeomTimer->isActive()){ availgeomTimer->stop(); }
330 availgeomTimer->start();
331 }
332
submitSessionGeom()333 void RootDesktopObject::submitSessionGeom(){
334 //TODO - read off the available geom from each ScreenObject and register that with NativeWindowSystem
335 }
336