Github每日精选(第31期):macOS 下的亮度和音量调节MonitorControl

MonitorControl

MonitorControl可以在 Mac 上控制显示器的亮度和音量,就好像它是原生 Apple 显示器一样。

主要的特点
  • 控制显示器的亮度、音量和对比度!
  • 显示亮度和音量的原生 OSD。
  • 支持多种亮度调节协议:外部显示器的 DDC(亮度、对比度、音量)、Apple 和内置显示器的原生 - Apple 协议、软件调节的 Gamma 表控制(推荐用于 OLED)、AirPlay、Sidecar 和 Display 的阴影控制链接设备。
  • 支持平滑的亮度过渡。
  • 无缝结合的硬件和软件调光将调光扩展到显示器上可用的最低亮度之外。
  • 同步内置和 Apple 屏幕的亮度 - 将环境光传感器和触控条引起的变化复制到非 Apple 外部显示器!
  • 使用单个滑块或键盘快捷键同步所有显示器。
  • 允许调暗至全黑(高级功能)。
  • 支持自定义键盘快捷键以及 Apple 键盘上的标准亮度和媒体键。
  • 数十种自定义选项可调整应用程序的内部运作以适应您的硬件和需求(不要忘记Show advanced settings在应用程序首选项中启用)。
  • 现代、时尚且高度可定制的菜单反映了 Big Sur 中引入的 Control Control 的设计。
  • 简单、不显眼的 UI 融入 macOS 的一般美学(甚至菜单图标也可以隐藏)。
  • 支持自动更新,轻松体验。
代码分析

对于在任务栏上的设计,该软件也怎么做了如下的出来,自绘了相关的关键部件。

override func drawBar(inside aRect: NSRect, flipped: Bool) {
      guard !DEBUG_MACOS10, #available(macOS 11.0, *) else {
        super.drawBar(inside: aRect, flipped: flipped)
        return
      }
      var maxValue: Float = self.floatValue
      var minValue: Float = self.floatValue

      if self.isHighlightDisplayItems {
        maxValue = max(self.displayHighlightItems.values.max() ?? 0, maxValue)
        minValue = min(self.displayHighlightItems.values.min() ?? 1, minValue)
      }

      let barRadius = aRect.height * 0.5 * (self.numOfTickmarks == 0 ? 1 : self.tickMarkKnobExtraRadiusMultiplier)
      let bar = NSBezierPath(roundedRect: aRect, xRadius: barRadius, yRadius: barRadius)
      self.barFillColor.setFill()
      bar.fill()

      let barFilledWidth = (aRect.width - aRect.height) * CGFloat(maxValue) + aRect.height
      let barFilledRect = NSRect(x: aRect.origin.x, y: aRect.origin.y, width: barFilledWidth, height: aRect.height)
      let barFilled = NSBezierPath(roundedRect: barFilledRect, xRadius: barRadius, yRadius: barRadius)
      self.barFilledFillColor.setFill()
      barFilled.fill()

      let knobMinX = aRect.origin.x + (aRect.width - aRect.height) * CGFloat(minValue)
      let knobMaxX = aRect.origin.x + (aRect.width - aRect.height) * CGFloat(maxValue)
      let knobRect = NSRect(x: knobMinX + (self.numOfTickmarks == 0 ? CGFloat(0) : self.tickMarkKnobExtraInset), y: aRect.origin.y, width: aRect.height + CGFloat(knobMaxX - knobMinX), height: aRect.height).insetBy(dx: self.numOfTickmarks == 0 ? CGFloat(0) : self.tickMarkKnobExtraInset, dy: 0)
      let knobRadius = knobRect.height * 0.5 * (self.numOfTickmarks == 0 ? 1 : self.tickMarkKnobExtraRadiusMultiplier)

      if self.numOfTickmarks > 0 {
        for i in 1 ... self.numOfTickmarks - 2 {
          let currentMarkLocation = CGFloat((Float(1) / Float(self.numOfTickmarks - 1)) * Float(i))
          let tickMarkBounds = NSRect(x: aRect.origin.x + aRect.height + self.tickMarkKnobExtraInset - knobRect.height + self.tickMarkKnobExtraInset * 2 + CGFloat(Float((aRect.width - self.tickMarkKnobExtraInset * 5) * currentMarkLocation)), y: aRect.origin.y + aRect.height * (1 / 3), width: 4, height: aRect.height / 3)
          let tickmark = NSBezierPath(roundedRect: tickMarkBounds, xRadius: 1, yRadius: 1)
          self.tickMarkColor.setFill()
          tickmark.fill()
        }
      }

      let knobAlpha = CGFloat(max(0, min(1, (minValue - 0.08) * 5)))
      for i in 1 ... 3 {
        let knobShadow = NSBezierPath(roundedRect: knobRect.offsetBy(dx: CGFloat(-1 * 2 * i), dy: 0), xRadius: knobRadius, yRadius: knobRadius)
        self.knobShadowColor.withAlphaComponent(self.knobShadowColor.alphaComponent * knobAlpha).setFill()
        knobShadow.fill()
      }

      let knob = NSBezierPath(roundedRect: knobRect, xRadius: knobRadius, yRadius: knobRadius)
      (self.isTracking ? self.knobFillColorTracking : self.knobFillColor).withAlphaComponent(knobAlpha).setFill()
      knob.fill()

      if self.isHighlightDisplayItems, self.displayHighlightItems.count > 2 {
        for currentMarkLocation in self.displayHighlightItems.values {
          let highlightKnobX = aRect.origin.x + (aRect.width - aRect.height) * CGFloat(currentMarkLocation)
          let highlightKnobRect = NSRect(x: highlightKnobX + (self.numOfTickmarks == 0 ? CGFloat(0) : self.tickMarkKnobExtraInset), y: aRect.origin.y, width: aRect.height, height: aRect.height).insetBy(dx: (self.numOfTickmarks == 0 ? CGFloat(0) : self.tickMarkKnobExtraInset) + CGFloat(self.numOfTickmarks == 0 ? 6 : 3), dy: CGFloat(self.numOfTickmarks == 0 ? 6 : 6))
          let highlightKnobRadius = highlightKnobRect.height * 0.5 * (self.numOfTickmarks == 0 ? 1 : self.tickMarkKnobExtraRadiusMultiplier)
          let highlightKnob = NSBezierPath(roundedRect: highlightKnobRect, xRadius: highlightKnobRadius, yRadius: highlightKnobRadius)
          let highlightDisplayIndicatorAlpha = CGFloat(max(0, min(1, (currentMarkLocation - 0.08) * 5)))
          self.highlightDisplayIndicatorColor.withAlphaComponent(self.highlightDisplayIndicatorColor.alphaComponent * highlightDisplayIndicatorAlpha).setFill()
          highlightKnob.fill()
        }
      }

      self.knobStrokeColor.withAlphaComponent(self.knobStrokeColor.alphaComponent * knobAlpha).setStroke()
      knob.stroke()
      self.barStrokeColor.setStroke()
      bar.stroke()
    }
  }

对于屏幕亮度的控制。

 func setSmoothBrightness(_ to: Float = -1, slow: Bool = false) -> Bool {
    guard app.sleepID == 0, app.reconfigureID == 0 else {
      self.savePref(self.smoothBrightnessTransient, for: .brightness)
      self.smoothBrightnessRunning = false
      os_log("Pushing brightness stopped for Display %{public}@ because of sleep or reconfiguration", type: .info, String(self.identifier))
      return false
    }
    if slow {
      self.smoothBrightnessSlow = true
    }
    var stepDivider: Float = 6
    if self.smoothBrightnessSlow {
      stepDivider = 16
    }
    var dontPushAgain = false
    if to != -1 {
      os_log("Pushing brightness towards goal of %{public}@ for Display  %{public}@", type: .info, String(to), String(self.identifier))
      let value = max(min(to, 1), 0)
      self.savePref(value, for: .brightness)
      self.brightnessSyncSourceValue = value
      self.smoothBrightnessSlow = slow
      if self.smoothBrightnessRunning {
        return true
      }
    }
    let brightness = self.readPrefAsFloat(for: .brightness)
    if brightness != self.smoothBrightnessTransient {
      if abs(brightness - self.smoothBrightnessTransient) < 0.01 {
        self.smoothBrightnessTransient = brightness
        os_log("Pushing brightness finished for Display  %{public}@", type: .info, String(self.identifier))
        dontPushAgain = true
        self.smoothBrightnessRunning = false
      } else if brightness > self.smoothBrightnessTransient {
        self.smoothBrightnessTransient += max((brightness - self.smoothBrightnessTransient) / stepDivider, 1 / 100)
      } else {
        self.smoothBrightnessTransient += min((brightness - self.smoothBrightnessTransient) / stepDivider, 1 / 100)
      }
      _ = self.setDirectBrightness(self.smoothBrightnessTransient, transient: true)
      if !dontPushAgain {
        self.smoothBrightnessRunning = true
        DispatchQueue.main.asyncAfter(deadline: .now() + 0.02) {
          _ = self.setSmoothBrightness()
        }
      }
    } else {
      os_log("No more need to push brightness for Display  %{public}@ (setting one final time)", type: .info, String(self.identifier))
      _ = self.setDirectBrightness(self.smoothBrightnessTransient, transient: true)
      self.smoothBrightnessRunning = false
    }
    self.swBrightnessSemaphore.signal()
    return true
  }

对于声音的控制。

  func stepVolume(isUp: Bool, isSmallIncrement: Bool) {
    guard !self.readPrefAsBool(key: .unavailableDDC, for: .audioSpeakerVolume) else {
      OSDUtils.showOsdVolumeDisabled(displayID: self.identifier)
      return
    }
    let currentValue = self.readPrefAsFloat(for: .audioSpeakerVolume)
    var muteValue: Int?
    let volumeOSDValue = self.calcNewValue(currentValue: currentValue, isUp: isUp, isSmallIncrement: isSmallIncrement)
    if self.readPrefAsInt(for: .audioMuteScreenBlank) == 1, volumeOSDValue > 0 {
      muteValue = 2
    } else if self.readPrefAsInt(for: .audioMuteScreenBlank) != 1, volumeOSDValue == 0 {
      muteValue = 1
    }
    let isAlreadySet = volumeOSDValue == self.readPrefAsFloat(for: .audioSpeakerVolume)
    if !isAlreadySet {
      if let muteValue = muteValue, self.readPrefAsBool(key: .enableMuteUnmute) {
        self.writeDDCValues(command: .audioMuteScreenBlank, value: UInt16(muteValue))
        self.savePref(muteValue, for: .audioMuteScreenBlank)
      }
      if !self.readPrefAsBool(key: .enableMuteUnmute) || volumeOSDValue != 0 {
        self.writeDDCValues(command: .audioSpeakerVolume, value: self.convValueToDDC(for: .audioSpeakerVolume, from: volumeOSDValue))
      }
    }
    if !self.readPrefAsBool(key: .hideOsd) {
      OSDUtils.showOsd(displayID: self.identifier, command: .audioSpeakerVolume, value: volumeOSDValue, roundChiclet: !isSmallIncrement)
    }
    if !isAlreadySet {
      self.savePref(volumeOSDValue, for: .audioSpeakerVolume)
      if let slider = self.sliderHandler[.audioSpeakerVolume] {
        slider.setValue(volumeOSDValue, displayID: self.identifier)
      }
    }
  }

你可能感兴趣的:(Github每日精选,macos,github,人工智能)