Skip to content

Commit

Permalink
v1.6.0 fixed multiple bugs: Discrete only is not always enforced, Doe…
Browse files Browse the repository at this point in the history
…s not correctly handle external displays, and refactored large parts of code to make it much safer.
  • Loading branch information
CodySchrank committed Apr 20, 2018
1 parent 47e2753 commit d7cdd66
Show file tree
Hide file tree
Showing 10 changed files with 202 additions and 105 deletions.
26 changes: 12 additions & 14 deletions gSwitch/AppDelegate.swift
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,13 @@ class AppDelegate: NSObject, NSApplicationDelegate {
DistributedNotificationCenter.default().postNotificationName(.KILLME, object: Bundle.main.bundleIdentifier, userInfo: nil, options: DistributedNotificationCenter.Options.deliverImmediately)
}

/** I like logs */
let console = ConsoleDestination()
let file = FileDestination()
file.logFileURL = URL(fileURLWithPath: "swiftybeaver.log") //logs to container/*/swiftybeaver.log
log.addDestination(console)
log.addDestination(file)

/** If we cant connect to gpu there is no point in continuing */
do {
try manager.connect()
Expand All @@ -42,28 +49,19 @@ class AppDelegate: NSObject, NSApplicationDelegate {
NSApplication.shared.terminate(self)
}

/** Juicy logs */
let console = ConsoleDestination()
let file = FileDestination()
file.logFileURL = URL(fileURLWithPath: "swiftybeaver.log") //logs to container/*/swiftybeaver.log
log.addDestination(console)
log.addDestination(file)

/** GPU Names are good */
manager.setGPUNames()

/** Lets listen to changes! */
listener.listen(manager: manager, processor: processer)

/**
TODO: Could add in ability to select mode with args here instead of hard .SetDynamic
*/

/** Lets set dynamic on startup */
if(manager.GPUMode(mode: .SetDynamic)) {
log.info("Initial Set Dynamic")
log.info("Initially set as Dynamic")
} else {
log.warning("Could not set dynamic")
//if it could connect but couldnt set idk if thats possible?
//if it is possible this should quit the program here and report error
log.error("Could not set dynamic")
}

/** Get current state so current gpu name exists for use in menu */
Expand All @@ -72,7 +70,7 @@ class AppDelegate: NSObject, NSApplicationDelegate {
/** Are there any hungry processes off the bat? Updates menu if so */
processer.updateProcessMenuList()

/** NotificationCenter wants to check the gpu too */
/** UserNotificationManager likes the gpu too */
notifications.inject(manager: manager)

/** Default prefs so shit works */
Expand Down
2 changes: 1 addition & 1 deletion gSwitch/BossyWindow.swift
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ import Foundation
import SwiftyBeaver

class BossyWindow: NSWindowController {
let log = SwiftyBeaver.self
internal let log = SwiftyBeaver.self

public func pushToFront() {
self.window?.center()
Expand Down
6 changes: 6 additions & 0 deletions gSwitch/Constants.swift
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,10 @@
// Created by Cody Schrank on 4/14/18.
// Copyright © 2018 CodySchrank. All rights reserved.
//
// GPUState, DispatchSelectors, and Features is from gfxCardStatus
// Copyright (c) 2010-2012, Cody Krieger
// All rights reserved.
//

import Foundation

Expand Down Expand Up @@ -94,6 +98,7 @@ struct Constants {
static let NOTIFICATION_QUEUE = "com.CodySchrank.GSwitch.GPUChangeNotificationQueue"
static let kCGDisplaySetModeFlag = (1 << 3)
static let kCGDisplayAddFlag = (1 << 4)
static let kCGDisplayRemoveFlag = (1 << 5)
static let launcherApplicationIdentifier = "com.CodySchrank.gSwitchLauncher"
static let GPU_CHANGE_NOTIFICATIONS = "gpuChangeNotifications"
static let APP_LOGIN_START = "appLoginStart"
Expand All @@ -104,6 +109,7 @@ extension Notification.Name {
static let checkForHungryProcesses = Notification.Name("checkForHungryProcesses")
static let updateProcessListInMenu = Notification.Name("updateProcessListInMenu")
static let probableGPUChange = Notification.Name("probableGPUChange")
static let externalDisplayConnect = Notification.Name("externalDisplayConnect")
static let startPolling = Notification.Name("startPolling")
static let stopPolling = Notification.Name("stopPolling")
static let KILLME = Notification.Name("killme")
Expand Down
41 changes: 30 additions & 11 deletions gSwitch/GPUListener.swift
Original file line number Diff line number Diff line change
Expand Up @@ -5,20 +5,25 @@
// Created by Cody Schrank on 4/15/18.
// Copyright © 2018 CodySchrank. All rights reserved.
//
// some logic is from gfxCardStatus
// https://github.com/codykrieger/gfxCardStatus/blob/master/LICENSE @ Jun 17, 2012
// Copyright (c) 2010-2012, Cody Krieger
// All rights reserved.
//

import Foundation
import CoreGraphics
import SwiftyBeaver

class GPUListener {
let log = SwiftyBeaver.self
private let log = SwiftyBeaver.self

var _notificationQueue: DispatchQueue?
var _manager: GPUManager?
var _processor: ProcessManager?
private var notificationQueue: DispatchQueue?
private var _manager: GPUManager?
private var _processor: ProcessManager?

init() {
self._notificationQueue = DispatchQueue.init(label: Constants.NOTIFICATION_QUEUE)
self.notificationQueue = DispatchQueue.init(label: Constants.NOTIFICATION_QUEUE)
}

public func listen(manager: GPUManager, processor: ProcessManager) {
Expand All @@ -38,34 +43,48 @@ class GPUListener {
NotificationCenter.default.post(name: .checkForHungryProcesses, object: nil)

if(this._manager!.CheckGPUStateAndisUsingIntegratedGPU() && this._manager!.requestedMode == SwitcherMode.ForceIntergrated) {
//calls .checkGPUState
this.log.info("NOTIFY?: Switched from desired integrated to discrete")
// calls .checkGPUState
this.log.info("Switched from desired integrated to discrete")
}


if Int(flags.rawValue) & Constants.kCGDisplaySetModeFlag > 0 {
/**
Hungry apps usually call the gpu when they start and when they exit
If a user is on integrated only this forces it back from discrete.
*/

this.log.info("Dedicated Graphics Card Called")
_ = this._processor?.getHungryProcesses()

this._notificationQueue?.async(execute: {
this.notificationQueue?.async(execute: {
sleep(1)

//calls .checkGPUState
// calls .checkGPUState

let isUsingIntegrated = this._manager!.CheckGPUStateAndisUsingIntegratedGPU()
let requestedMode = this._manager!.requestedMode

if(!isUsingIntegrated && requestedMode == SwitcherMode.ForceIntergrated) {
if(this._manager!.GPUMode(mode: .ForceIntergrated)) {
this.log.info("NOTIFY?: Forced integrated GPU From dedicated GPU")
this.log.info("Forced integrated GPU From dedicated GPU")
}
} else {
// usually gets called when change but sometimes gets called when no change?
this.log.info("NOTIFY: GPU maybe Changed")
this.log.verbose("NOTIFY: GPU maybe Changed")
NotificationCenter.default.post(name: .probableGPUChange, object: this._manager?.currentGPU)
}
})
}

if Int(flags.rawValue) & Constants.kCGDisplayRemoveFlag > 0 {
/**
usually gets called when switched. If I could get a flag that only triggered
when the display was disconnected I could save the last desired state that
the user selected and put them back on it.
(because dynamic is forced when a display is connected)
*/
}
}


Expand Down
55 changes: 31 additions & 24 deletions gSwitch/GPUManager.swift
Original file line number Diff line number Diff line change
Expand Up @@ -5,19 +5,25 @@
// Created by Cody Schrank on 4/14/18.
// Copyright © 2018 CodySchrank. All rights reserved.
//
// some logic is from gfxCardStatus
// https://github.com/codykrieger/gfxCardStatus/blob/master/LICENSE @ Jun 17, 2012
// Copyright (c) 2010-2012, Cody Krieger
// All rights reserved.
//

import Foundation
import IOKit
import SwiftyBeaver

class GPUManager {
let log = SwiftyBeaver.self
private let log = SwiftyBeaver.self

public var integratedName: String?
public var discreteName: String?
public var currentGPU: String?
public var requestedMode: SwitcherMode?

var _connect: io_connect_t = IO_OBJECT_NULL;
var integratedName: String?
var discreteName: String?
var currentGPU: String?
var requestedMode: SwitcherMode?
private var _connect: io_connect_t = IO_OBJECT_NULL;

public func setGPUNames() {
let gpus = getGpuNames()
Expand Down Expand Up @@ -112,27 +118,28 @@ class GPUManager {
let integrated = CheckGPUStateAndisUsingIntegratedGPU()
log.info("Requesting integrated, are we integrated? \(integrated)")

if (mode == .ForceIntergrated && !integrated) {
if !integrated {
status = SwitchGPU(connect: connect)
}

case .ForceDiscrete:
let discrete = !CheckGPUStateAndisUsingIntegratedGPU()
log.info("Requesting discrete, are we discrete? \(discrete)")
log.info("Requesting discrete")

/**
Why not use the way macos designed it?
And leaves the user the ability to change back via system pref
*/
/** Essientialy ticks and unticks the box in system prefs, which by design forces discrete */

if (mode == .ForceDiscrete && !discrete) {
_ = setFeatureInfo(connect: connect, feature: Features.Policy, enabled: true)
_ = setSwitchPolicy(connect: connect)

status = setDynamicSwitching(connect: connect, enabled: false)
}
_ = setFeatureInfo(connect: connect, feature: Features.Policy, enabled: true)
_ = setSwitchPolicy(connect: connect)

status = setDynamicSwitching(connect: connect, enabled: true)

// give the gpu a second to switch
sleep(1)

status = setDynamicSwitching(connect: connect, enabled: false)
case .SetDynamic:
// Set switch policy back, make the MBP think it's an auto switching one once again
log.info("Requesting Dynamic")

/** Set switch policy back, makes it think its on auto switching */
_ = setFeatureInfo(connect: connect, feature: Features.Policy, enabled: true)
_ = setSwitchPolicy(connect: connect)

Expand Down Expand Up @@ -210,9 +217,9 @@ class GPUManager {
);

if kernResult == KERN_SUCCESS {
log.info("Modified state with \(state)")
log.verbose("Modified state with \(state)")
} else {
log.info("ERROR: Set state returned \(kernResult)")
log.error("ERROR: Set state returned \(kernResult)")
}

return kernResult == KERN_SUCCESS
Expand Down Expand Up @@ -245,9 +252,9 @@ class GPUManager {
);

if kernResult == KERN_SUCCESS {
log.info("Successfully got state, count \(outputCount), value \(output)")
log.verbose("Successfully got state, count \(outputCount), value \(output)")
} else {
log.info("ERROR: Get state returned \(kernResult)")
log.error("ERROR: Get state returned \(kernResult)")
}

return output
Expand Down
2 changes: 1 addition & 1 deletion gSwitch/GPUViewController.swift
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ class GPUView: NSView {

/** We use a hidden view to poll for hungry processes or possibly other information like vram */

let log = SwiftyBeaver.self
private let log = SwiftyBeaver.self

override func draw(_ dirtyRect: NSRect) {
super.draw(dirtyRect)
Expand Down
2 changes: 0 additions & 2 deletions gSwitch/GSProcess.m
Original file line number Diff line number Diff line change
Expand Up @@ -6,8 +6,6 @@
// Copyright (c) 2010-2012, Cody Krieger
// All rights reserved.
//
// Original task list functionality Copyright 2010 Thierry Coppey.
//

#import "GSProcess.h"

Expand Down
37 changes: 21 additions & 16 deletions gSwitch/ProcessManager.swift
Original file line number Diff line number Diff line change
Expand Up @@ -22,14 +22,14 @@ struct Process {
}

class ProcessManager {
let log = SwiftyBeaver.self
private let log = SwiftyBeaver.self

/**
At this time not doing any polling but its possible.
Maybe poll for more useful information like vram or gpu usage?
Maybe poll for more useful information like vram or gpu usage when the menu is open
*/

var pollTimer: Timer?
private var pollTimer: Timer?

init() {
NotificationCenter.default.addObserver(self, selector: #selector(_updateProcessMenuList(notification:)), name: .checkForHungryProcesses, object: nil)
Expand Down Expand Up @@ -62,24 +62,29 @@ class ProcessManager {
self.updateProcessMenuList()
}

/** Need to start on main thread */
/** Not currently used */
@objc private func startPoll(notification: NSNotification) {
if self.pollTimer == nil {
log.info("Starting Poll")
self.pollTimer = Timer.scheduledTimer(
timeInterval: 2,
target : self,
selector : #selector(_updateProcessMenuList(notification:)),
userInfo : nil,
repeats : false)
DispatchQueue.main.async {
if self.pollTimer == nil {
self.log.info("Starting Poll")
self.pollTimer = Timer.scheduledTimer(
timeInterval: 2,
target : self,
selector : #selector(self._updateProcessMenuList(notification:)),
userInfo : nil,
repeats : false)
}
}
}

/** Not currently used */
@objc private func stopPoll(notification: NSNotification) {
if pollTimer != nil {
log.info("Stopping Poll")
pollTimer?.invalidate()
pollTimer = nil
DispatchQueue.main.async {
if self.pollTimer != nil {
self.log.info("Stopping Poll")
self.pollTimer?.invalidate()
self.pollTimer = nil
}
}
}
}
Loading

0 comments on commit d7cdd66

Please sign in to comment.