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