diff --git a/platform/platform-impl/src/com/intellij/util/io/power.kt b/platform/platform-impl/src/com/intellij/util/io/power.kt
new file mode 100644
index 000000000000..e05389fc0b3f
--- /dev/null
+++ b/platform/platform-impl/src/com/intellij/util/io/power.kt
@@ -0,0 +1,238 @@
+// Copyright 2000-2018 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE file.
+package com.intellij.util.io
+
+import com.intellij.jna.JnaLoader
+import com.intellij.openapi.diagnostic.Logger
+import com.intellij.openapi.util.SystemInfo
+import com.sun.jna.Library
+import com.sun.jna.Native
+import com.sun.jna.Pointer
+import com.sun.jna.Structure
+import com.sun.jna.win32.StdCallLibrary
+import java.io.BufferedReader
+import java.io.File
+import java.io.FileReader
+import java.io.IOException
+
+private val LOG = Logger.getInstance(PowerStatus::class.java)
+
+enum class PowerStatus {
+ UNKNOWN, AC, BATTERY;
+
+ companion object {
+ @JvmStatic fun getPowerStatus(): PowerStatus =
+ try { service.status() }
+ catch (t: Throwable) {
+ LOG.warn(t)
+ UNKNOWN
+ }
+ }
+}
+
+//
+private interface PowerService {
+ fun status(): PowerStatus
+}
+
+private val service: PowerService by lazy {
+ try {
+ when {
+ SystemInfo.isWindows && JnaLoader.isLoaded() -> WinPowerService()
+ SystemInfo.isMac && JnaLoader.isLoaded() -> MacPowerService()
+ SystemInfo.isLinux -> LinuxPowerService()
+ else -> NullPowerService()
+ }
+ }
+ catch (t: Throwable) {
+ LOG.warn(t)
+ NullPowerService()
+ }
+}
+
+private class NullPowerService : PowerService {
+ override fun status() = PowerStatus.UNKNOWN
+}
+//
+
+//
+@Suppress("ClassName", "PropertyName", "FunctionName", "unused")
+private class WinPowerService : PowerService {
+ override fun status(): PowerStatus {
+ val status = SYSTEM_POWER_STATUS()
+ if (!kernel32.GetSystemPowerStatus(status)) {
+ throw IOException("GetSystemPowerStatus(): ${kernel32.GetLastError()}")
+ }
+ else {
+ if (LOG.isDebugEnabled) LOG.debug("ACLineStatus=${status.ACLineStatus}")
+ return when (status.ACLineStatus.toInt()) {
+ 0 -> PowerStatus.BATTERY
+ 1 -> PowerStatus.AC
+ else -> PowerStatus.UNKNOWN
+ }
+ }
+ }
+
+ class SYSTEM_POWER_STATUS : Structure() {
+ override fun getFieldOrder() =
+ listOf("ACLineStatus", "BatteryFlag", "BatteryLifePercent", "SystemStatusFlag", "BatteryLifeTime", "BatteryFullLifeTime")
+
+ @JvmField var ACLineStatus: Byte = 0
+ @JvmField var BatteryFlag: Byte = 0
+ @JvmField var BatteryLifePercent: Byte = 0
+ @JvmField var SystemStatusFlag: Byte = 0
+ @JvmField var BatteryLifeTime: Int = 0
+ @JvmField var BatteryFullLifeTime: Int = 0
+ }
+
+ private interface Kernel32 : StdCallLibrary {
+ fun GetSystemPowerStatus(result: SYSTEM_POWER_STATUS): Boolean
+ fun GetLastError(): Int
+ }
+
+ private val kernel32 = Native.loadLibrary("kernel32", Kernel32::class.java)
+}
+//
+
+//
+@Suppress("FunctionName", "unused")
+private class MacPowerService : PowerService {
+ /**
+ * In IOKit, "power sources" include batteries and UPS devices.
+ * The method returns "BATTERY" in following cases:
+ * - when a system has a battery which is discharging
+ * - when a system has no discharging batteries but has a discharging UPS
+ */
+ override fun status() =
+ ioKit.IOPSCopyPowerSourcesInfo().use { psBlob ->
+ ioKit.IOPSCopyPowerSourcesList(psBlob).use { psList ->
+ var batteryState: PowerStatus? = null
+ var upsState: PowerStatus? = null
+
+ val count = ioKit.CFArrayGetCount(psList)
+ if (LOG.isDebugEnabled) LOG.debug("count=${count}")
+ for (i in 0 until count) {
+ val ps = ioKit.IOPSGetPowerSourceDescription(psBlob, ioKit.CFArrayGetValueAtIndex(psList, i))
+ if (isTrue(ioKit.CFDictionaryGetValue(ps, kIOPSIsPresentKey))) {
+ val type = ioKit.CFDictionaryGetValue(ps, kIOPSTypeKey)
+ val state = ioKit.CFDictionaryGetValue(ps, kIOPSPowerSourceStateKey)
+ if (LOG.isDebugEnabled) LOG.debug("${i}: type='${str(type)}' state='${str(state)}'")
+ if (strEquals(type, kIOPSInternalBatteryType)) {
+ batteryState = if (strEquals(state, kIOPSBatteryPowerValue)) PowerStatus.BATTERY else PowerStatus.AC
+ break
+ }
+ else if (strEquals(type, kIOPSUPSType) && upsState === null) {
+ upsState = if (strEquals(state, kIOPSBatteryPowerValue)) PowerStatus.BATTERY else PowerStatus.AC
+ }
+ }
+ }
+
+ when {
+ batteryState === PowerStatus.BATTERY -> PowerStatus.BATTERY
+ upsState !== null -> upsState
+ else -> PowerStatus.AC
+ }
+ }
+ } ?: PowerStatus.UNKNOWN
+
+ private interface IOKit : Library {
+ fun IOPSCopyPowerSourcesInfo(): Pointer?
+ fun IOPSCopyPowerSourcesList(psBlob: Pointer): Pointer?
+ fun IOPSGetPowerSourceDescription(psBlob: Pointer, psName:Pointer): Pointer
+
+ fun CFRelease(p: Pointer)
+ fun CFArrayGetCount(array: Pointer): Long
+ fun CFArrayGetValueAtIndex(array: Pointer, idx: Long): Pointer
+ fun CFDictionaryGetValue(dict: Pointer, key: Pointer): Pointer?
+ fun CFStringCreateWithCharacters(alloc: Pointer?, chars: CharArray, numChars: Long): Pointer
+ fun CFStringCompare(str1: Pointer, str2: Pointer, flags: Long): Long
+ fun CFStringGetLength(str: Pointer): Long
+ fun CFStringGetCharacters(str: Pointer, range: CFRange, buffer: CharArray)
+ fun CFBooleanGetValue(ref: Pointer): Short
+ }
+
+ class CFRange : Structure(), Structure.ByValue {
+ override fun getFieldOrder() = listOf("location", "length")
+
+ @JvmField var location: Long = 0
+ @JvmField var length: Long = 0
+ }
+
+ private val ioKit: IOKit = Native.loadLibrary("IOKit", IOKit::class.java)
+ private val kIOPSIsPresentKey = CFSTR("Is Present")
+ private val kIOPSTypeKey = CFSTR("Type")
+ private val kIOPSInternalBatteryType = CFSTR("InternalBattery")
+ private val kIOPSUPSType = CFSTR("UPS")
+ private val kIOPSPowerSourceStateKey = CFSTR("Power Source State")
+ private val kIOPSBatteryPowerValue = CFSTR("Battery Power")
+ private val kCFCompareEqualTo = 0L
+
+ private fun CFSTR(str: String) = ioKit.CFStringCreateWithCharacters(null, str.toCharArray(), str.length.toLong())
+
+ private fun isTrue(p: Pointer?) = p !== null && ioKit.CFBooleanGetValue(p).toInt() != 0
+
+ private fun strEquals(p: Pointer?, str: Pointer) = p !== null && ioKit.CFStringCompare(p, str, 0L) == kCFCompareEqualTo
+
+ private fun str(str: Pointer?): String =
+ if (str === null) ""
+ else {
+ val range = CFRange()
+ range.length = ioKit.CFStringGetLength(str)
+ val buffer = CharArray(range.length.toInt())
+ ioKit.CFStringGetCharacters(str, range, buffer)
+ String(buffer)
+ }
+
+ private inline fun Pointer?.use(block: (Pointer) -> T): T? =
+ if (this === null) null
+ else try { block(this) } finally { ioKit.CFRelease(this) }
+}
+//
+
+//
+private class LinuxPowerService : PowerService {
+ /**
+ * Returns "AC" if there is at least one online source of type "Mains".
+ * Returns "BATTERY" if there is at least one source of type "Battery" in a discharging state.
+ * UPSes doesn't seem to be represented via sysfs.
+ * See [https://github.com/torvalds/linux/blob/master/drivers/power/supply/power_supply_sysfs.c].
+ */
+ override fun status(): PowerStatus {
+ val devices = classDirectory.listFiles() ?: throw IOException("can't enumerate devices")
+ if (LOG.isDebugEnabled) LOG.debug("devices=${devices.size}")
+
+ var online = false
+ var discharging = false
+
+ for (device in devices) {
+ val type = read(device, "type")
+ if (LOG.isDebugEnabled) LOG.debug("${device.name} type=${type}")
+ if (type == "Mains") {
+ val state = read(device, "online")
+ if (LOG.isDebugEnabled) LOG.debug(" online=${state}")
+ if (state == "1") online = true
+ }
+ else if (type == "Battery") {
+ val state = read(device, "status")
+ if (LOG.isDebugEnabled) LOG.debug(" status=${state}")
+ if (state == "Discharging") discharging = true
+ }
+ }
+
+ return when {
+ online -> PowerStatus.AC
+ discharging -> PowerStatus.BATTERY
+ else -> PowerStatus.UNKNOWN
+ }
+ }
+
+ private val classDirectory = File("/sys/class/power_supply")
+
+ init {
+ if (!classDirectory.isDirectory) throw IOException("not a directory: ${classDirectory}")
+ }
+
+ private fun read(device: File, key: String) =
+ try { BufferedReader(FileReader(File(device, key))).use { it.readLine() } }
+ catch (e: IOException) { "-" }
+}
+//
\ No newline at end of file
diff --git a/platform/platform-impl/src/com/intellij/util/system/PowerStatus.kt b/platform/platform-impl/src/com/intellij/util/system/PowerStatus.kt
deleted file mode 100644
index 0eca2b68e2a9..000000000000
--- a/platform/platform-impl/src/com/intellij/util/system/PowerStatus.kt
+++ /dev/null
@@ -1,110 +0,0 @@
-// Copyright 2000-2018 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE file.
-package com.intellij.util.system
-
-import com.intellij.execution.ExecutionException
-import com.intellij.execution.configurations.GeneralCommandLine
-import com.intellij.execution.util.ExecUtil
-import com.intellij.jna.JnaLoader
-import com.intellij.openapi.util.SystemInfo
-import com.intellij.openapi.util.io.FileUtil
-import com.intellij.openapi.util.registry.Registry
-import com.sun.jna.Native
-import com.sun.jna.Structure
-import com.sun.jna.win32.StdCallLibrary
-import java.io.IOException
-
-enum class PowerStatus {
- UNKNOWN, AC_POWER, BATTERY
-}
-
-fun getPowerStatus(): PowerStatus {
- return when {
- SystemInfo.isWindows -> getWindowsPowerStatus()
- SystemInfo.isLinux -> getLinuxPowerStatus()
- SystemInfo.isMac -> getMacPowerStatus()
- else -> PowerStatus.UNKNOWN
- }
-}
-
-private fun getWindowsPowerStatus(): PowerStatus {
- if (!JnaLoader.isLoaded()) return PowerStatus.UNKNOWN
- val systemPowerStatus = SYSTEM_POWER_STATUS()
- if (!kernel32.GetSystemPowerStatus(systemPowerStatus)) return PowerStatus.UNKNOWN
- return when(systemPowerStatus.ACLineStatus.toInt()) {
- 1 -> PowerStatus.AC_POWER
- 0 -> PowerStatus.BATTERY
- else -> PowerStatus.UNKNOWN
- }
-}
-
-class SYSTEM_POWER_STATUS : Structure() {
- override fun getFieldOrder(): List {
- return listOf("ACLineStatus", "BatteryFlag", "BatteryLifePercent", "SystemStatusFlag", "BatteryLifeTime", "BatteryFullLifeTime")
- }
-
- @JvmField var ACLineStatus: Byte = 0
- @JvmField var BatteryFlag: Byte = 0
- @JvmField var BatteryLifePercent: Byte = 0
- @JvmField var SystemStatusFlag: Byte = 0
- @JvmField var BatteryLifeTime: Int = 0
- @JvmField var BatteryFullLifeTime: Int = 0
-}
-
-private interface Kernel32 : StdCallLibrary {
- fun GetSystemPowerStatus(result: SYSTEM_POWER_STATUS): Boolean
-}
-
-private val kernel32 by lazy { Native.loadLibrary("kernel32", Kernel32::class.java) }
-
-// https://github.com/Goles/Battery/blob/master/battery
-private fun getLinuxPowerStatus(): PowerStatus {
- val file = batteryFilePath ?: return PowerStatus.UNKNOWN
- try {
- return if (FileUtil.loadFile(file).trim() == "Discharging") PowerStatus.BATTERY else PowerStatus.AC_POWER
- }
- catch (e: IOException) {
- return PowerStatus.UNKNOWN;
- }
-}
-
-private val batteryFilePath by lazy {
- FileUtil.findFirstThatExist("/sys/class/power_supply/BAT0/status")
-}
-
-fun getMacPowerStatus(): PowerStatus {
- val currentTime = System.currentTimeMillis()
- lastMacPowerStatus?.let {
- if (currentTime - lastMacPowerStatusUpdateTimestamp > Registry.intValue("ide.mac.power.status.refresh")) {
- return it
- }
- }
- val status = updateMacPowerStatus()
- lastMacPowerStatusUpdateTimestamp = currentTime
- lastMacPowerStatus = status
- return status
-}
-
-private var lastMacPowerStatus: PowerStatus? = null
-private var lastMacPowerStatusUpdateTimestamp: Long = 0
-
-private fun updateMacPowerStatus(): PowerStatus {
- try {
- val output = ExecUtil.execAndGetOutput(GeneralCommandLine("/usr/bin/pmset", "-g", "batt"))
- if (output.exitCode != 0 || output.stderr.isNotEmpty()) return PowerStatus.UNKNOWN
- return when {
- "'AC Power'" in output.stdout -> PowerStatus.AC_POWER
- "'Battery Power'" in output.stdout -> PowerStatus.BATTERY
- else -> PowerStatus.UNKNOWN
- }
- }
- catch (e: ExecutionException) {
- return PowerStatus.UNKNOWN
- }
-}
-
-fun main(args: Array) {
- val startTime = System.nanoTime()
- val powerStatus = getPowerStatus()
- val elapsed = (System.nanoTime() - startTime) / 1000
- println("The current power status is $powerStatus, retrieved in $elapsed useŃ")
-}
diff --git a/platform/platform-tests/testSrc/com/intellij/util/io/PowerStatusTest.java b/platform/platform-tests/testSrc/com/intellij/util/io/PowerStatusTest.java
new file mode 100644
index 000000000000..3c601581c602
--- /dev/null
+++ b/platform/platform-tests/testSrc/com/intellij/util/io/PowerStatusTest.java
@@ -0,0 +1,21 @@
+// Copyright 2000-2018 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE file.
+package com.intellij.util.io;
+
+import com.intellij.testFramework.UsefulTestCase;
+import org.junit.Test;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNotEquals;
+
+public class PowerStatusTest {
+ @Test
+ public void test() {
+ PowerStatus status = PowerStatus.getPowerStatus();
+ if (UsefulTestCase.IS_UNDER_TEAMCITY) {
+ assertEquals(PowerStatus.AC, status);
+ }
+ else {
+ assertNotEquals(PowerStatus.UNKNOWN, status);
+ }
+ }
+}
\ No newline at end of file