diff --git a/code/apps/MunkiStatus/MunkiStatus.xcodeproj/project.pbxproj b/code/apps/MunkiStatus/MunkiStatus.xcodeproj/project.pbxproj index f98c5487..b1e37e41 100644 --- a/code/apps/MunkiStatus/MunkiStatus.xcodeproj/project.pbxproj +++ b/code/apps/MunkiStatus/MunkiStatus.xcodeproj/project.pbxproj @@ -57,6 +57,7 @@ C046261C1A00016200AF1E48 /* it */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = it; path = it.lproj/InfoPlist.strings; sourceTree = ""; }; C046261D1A00019800AF1E48 /* it */ = {isa = PBXFileReference; lastKnownFileType = file.xib; name = it; path = it.lproj/MainMenu.xib; sourceTree = ""; }; C05C3CEE188391F200095E65 /* munki.py */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.script.python; name = munki.py; path = MunkiStatus/munki.py; sourceTree = SOURCE_ROOT; }; + C06137A51C9CB3BD00EC298E /* BorderlessWindow.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = BorderlessWindow.h; sourceTree = ""; }; C07E956C1913ECEF00B40B9A /* fr */ = {isa = PBXFileReference; lastKnownFileType = file.xib; name = fr; path = fr.lproj/MainMenu.xib; sourceTree = ""; }; C07E956D1913ECEF00B40B9A /* fr */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = fr; path = fr.lproj/InfoPlist.strings; sourceTree = ""; }; C07E956E1913ECF400B40B9A /* de */ = {isa = PBXFileReference; lastKnownFileType = file.xib; name = de; path = de.lproj/MainMenu.xib; sourceTree = ""; }; @@ -157,6 +158,7 @@ C09004F916CDD84E00BE34CE /* MunkiStatus */ = { isa = PBXGroup; children = ( + C06137A51C9CB3BD00EC298E /* BorderlessWindow.h */, C094B6D31891826700E06897 /* BorderlessWindow.m */, C094B6D41891826700E06897 /* ScaledImageView.h */, C094B6D51891826700E06897 /* ScaledImageView.m */, diff --git a/code/apps/MunkiStatus/MunkiStatus/MSUStatusWindowController.py b/code/apps/MunkiStatus/MunkiStatus/MSUStatusWindowController.py index 0402fcb2..ee71ef15 100644 --- a/code/apps/MunkiStatus/MunkiStatus/MSUStatusWindowController.py +++ b/code/apps/MunkiStatus/MunkiStatus/MSUStatusWindowController.py @@ -22,6 +22,8 @@ from objc import YES, NO, IBAction, IBOutlet, nil from PyObjCTools import AppHelper import os +from platform import release + import munki import FoundationPlist @@ -86,6 +88,7 @@ class MSUStatusWindowController(NSObject): timeout_counter = 0 saw_process = False managedsoftwareupdate_pid = None + window_level = NSScreenSaverWindowLevel - 1 @IBAction def stopBtnClicked_(self, sender): @@ -131,6 +134,9 @@ class MSUStatusWindowController(NSObject): # set self.receiving_notifications to False so our process monitoring # thread will exit self.receiving_notifications = False + + def windowDidResignMain_(self, notification): + self.window.orderFrontRegardless() def managedsoftwareupdateStarted_(self, notification): '''Called when we get a @@ -145,9 +151,27 @@ class MSUStatusWindowController(NSObject): com.googlecode.munki.managedsoftwareupdate.ended notification''' NSLog('managedsoftwareupdate pid %s ended' % notification.userInfo().get('pid')) + + def setWindowLevel(self): + '''Sets our NSWindowLevel. Works around issues with the loginwindow + PolicyBanner in 10.11+ Some code based on earlier work by Pepijn + Bruienne''' + # Get our Darwin major version + clientOS = release().split('.')[0] + havePolicyBanner = False + for test_file in ['/Library/Security/PolicyBanner.txt', + '/Library/Security/PolicyBanner.rtf', + '/Library/Security/PolicyBanner.rtfd']: + if os.path.exists(test_file): + havePolicyBanner = True + break + # bump our NSWindowLevel if we have a PolicyBanner in ElCap+ + if havePolicyBanner and clientOS >= 15: + self.window_level = NSScreenSaverWindowLevel def initStatusSession(self): '''Initialize our status session''' + self.setWindowLevel() consoleuser = munki.getconsoleuser() if consoleuser == None or consoleuser == u"loginwindow": self.displayBackdropWindow() @@ -156,7 +180,7 @@ class MSUStatusWindowController(NSObject): if consoleuser == None or consoleuser == u"loginwindow": # needed so the window can show over the loginwindow self.window.setCanBecomeVisibleWithoutLogin_(True) - self.window.setLevel_(NSScreenSaverWindowLevel - 1) + self.window.setLevel_(self.window_level) self.window.center() self.messageFld.setStringValue_( NSLocalizedString(u"Starting…", None)) @@ -235,7 +259,7 @@ class MSUStatusWindowController(NSObject): '''Draw a window that covers the login UI''' if self.backdropWindow: self.backdropWindow.setCanBecomeVisibleWithoutLogin_(True) - self.backdropWindow.setLevel_(NSStatusWindowLevel) + self.backdropWindow.setLevel_(self.window_level) screenRect = NSScreen.mainScreen().frame() self.backdropWindow.setFrame_display_(screenRect, True) @@ -258,12 +282,24 @@ class MSUStatusWindowController(NSObject): self.backdropWindow.setAlphaValue_(0.0) self.backdropWindow.orderFrontRegardless() self.backdropWindow.animator().setAlphaValue_(1.0) + + # preserve the relative ordering of the backdrop window and the status window + # IOW, clicking the backdrop window will not bring it in front of the status window + self.backdropWindow.addChildWindow_ordered_(self.window, NSWindowAbove) + def updateStatus_(self, notification): '''Called when we get a com.googlecode.munki.managedsoftwareupdate.statusUpdate notification; update our status display with information from the notification''' + if self.window_level == NSScreenSaverWindowLevel: + # we're at the loginwindow, there is a PolicyBanner, and we're running + # under 10.11+. Make sure we're in the front. + if not NSApp.isActive(): + NSApp.activateIgnoringOtherApps_(YES) + self.window.orderFrontRegardless() + self.got_status_update = True info = notification.userInfo() # explictly get keys from info object; PyObjC in Mountain Lion @@ -288,6 +324,7 @@ class MSUStatusWindowController(NSObject): command = info.get('command') if command == 'activate': + NSApp.activateIgnoringOtherApps_(YES) self.window.orderFrontRegardless() elif command == 'showRestartAlert': # clean up timer