2014/8/6

【C#】設定/查詢螢幕背光

  為了要能用C#控制螢幕背光,跟CreateFileDeviceIoControl函數奮戰大半天,一直搞不定DeviceIoControl函數使用,後來幸運的在網路上找到有人寫好class可以直接調用,之後有需要用到DeviceIoControl函數功能也能參考這class的做法。

調用方法很簡單:
1
2
3
LCDBrightness lcd = new LCDBrightness();
lcd.SetBrightness(100);                                 // Set backlight full on
LCDBrightness.Brightness value = lcd.GetBrightness();   // Query backlight value

原始class內容為:
  1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
using Microsoft.VisualBasic;
using System;
using System.Collections;
using System.Data;
using System.Diagnostics;
using System.Drawing;
using System.Windows.Forms;
using System.Runtime.InteropServices;
/// <summary>
/// Permet de contrA’ler la luminositAc de l'Accran LCD (sous Windows XP SP2 et ultAcrieur)
/// </summary>
/// <remarks>Sous rAcserve que le driver de la carte graphique et/ou de l'Accran supporte cette interface</remarks>
namespace LCDBrightness
{
  public class LCDBrightness
  {
    //Ouvre un fichier ou un pAcriphAcrique
    [DllImport("kernel32")]
    private static extern IntPtr CreateFile (
        string lpFileName,
        uint dwDesiredAccess,
        int dwShareMode,
        IntPtr lpSecurityAttributes,
        int dwCreationDisposition,
        int dwFlagsAndAttributes,
        IntPtr hTemplateFile);

    //accA‥s en lecture, partagAc et ouverture uniquement d'un pAcriphAcrique existant
    private const uint GENERIC_READ = 0x80000000U;
    private const int SHARE_ALL = 0x7;
    private const int OPEN_EXISTING = 0x3;

    //indique l'Acclairage en mode d'alimentation AC ou DC ou les deux
    private const int DISPLAYPOLICY_AC = 1;
    private const int DISPLAYPOLICY_DC = 2;
    private const int DISPLAYPOLICY_BOTH = DISPLAYPOLICY_AC | DISPLAYPOLICY_DC;

    /// <summary>
    /// Indique les niveaux d'Acclairage en fonction du mode d'alimentation
    /// </summary>
    /// <remarks></remarks>
    public class Brightness
    {
      /// <summary>
      /// Eclairage en mode Alimentation
      /// </summary>
      /// <remarks></remarks>
      public byte AC;
      /// <summary>
      /// Eclairage en mode Batterie
      /// </summary>
      /// <remarks></remarks>
      public byte DC;
    }

    /// <summary>
    /// Contient les donnAces brutes des niveaux d'Acclairage
    /// </summary>
    /// <remarks></remarks>
    private struct DISPLAY_BRIGHTNESS
    {
      public byte ucDisplayPolicy;
      public byte ucACBrightness;
      public byte ucDCBrightness;
    }

    //envoie une requAate A  un pAcriphAcrique
    [DllImport("kernel32")]
    private static extern bool DeviceIoControl (
        IntPtr hDevice,
        int dwIoControlCode,
        ref DISPLAY_BRIGHTNESS lpInBuffer,
        int nInBufferSize,
        IntPtr lpOutBuffer,
        int nOutBufferSize,
        ref int lpBytesReturned,
        IntPtr lpOverlapped);
    [DllImport("kernel32")]
    private static extern bool DeviceIoControl (
        IntPtr hDevice,
        int dwIoControlCode,
        IntPtr lpInBuffer,
        int nInBufferSize,
        ref DISPLAY_BRIGHTNESS lpOutBuffer,
        int nOutBufferSize,
        ref int lpBytesReturned,
        IntPtr lpOverlapped);
    [DllImport("kernel32")]
    private static extern bool DeviceIoControl (
        IntPtr hDevice,
        int dwIoControlCode,
        IntPtr lpInBuffer,
        int nInBufferSize,
        [MarshalAs(UnmanagedType.LPArray)] byte[] lpOutBuffer,
        int nOutBufferSize,
        ref int lpBytesReturned,
        IntPtr lpOverlapped);

    //ferme un fichier ou un pAcriphAcrique
    [DllImport("kernel32")]
    private static extern int CloseHandle(IntPtr hObject);

#region IOCTL
    private const int FILE_DEVICE_VIDEO = 0x23;

    private const int FILE_ANY_ACCESS = 0;
    private const int FILE_SPECIAL_ACCESS = FILE_ANY_ACCESS;

    private const int METHOD_BUFFERED = 0;
    private const int METHOD_NEITHER = 3;

    private const int ERROR_INSUFFICIENT_BUFFER = 122;

    /// <summary>
    /// GA‥re la dAcfinition d'un code de requAate d'un pAcriphAcrique en fonction de ses catAcgories
    /// </summary>
    /// <param name="dwDeviceType">Type de pAcriphAcrique</param>
    /// <param name="dwFunction">RequAate</param>
    /// <param name="dwMethod">MActhode de la requAate</param>
    /// <param name="dwAccess">Mode d'accA‥s</param>
    /// <returns></returns>
    /// <remarks></remarks>
    private static int CTL_CODE(int dwDeviceType, int dwFunction, int dwMethod, int dwAccess)
    {
      return ((dwDeviceType) << 16) | ((dwAccess) << 14) | ((dwFunction) << 2) | (dwMethod);
    }

    //requAate de lecture des niveaux d'Aclcairage supportAcs
    private static int IOCTL_VIDEO_QUERY_SUPPORTED_BRIGHTNESS = CTL_CODE(FILE_DEVICE_VIDEO, 293, METHOD_BUFFERED, FILE_ANY_ACCESS);
    //requAate de lecture des niveaux d'Acclairage en cours
    private static int IOCTL_VIDEO_QUERY_DISPLAY_BRIGHTNESS = CTL_CODE(FILE_DEVICE_VIDEO, 294, METHOD_BUFFERED, FILE_ANY_ACCESS);
    //requAate de dAcfinition des niveaux d'Acclairage en cours
    private static int IOCTL_VIDEO_SET_DISPLAY_BRIGHTNESS = CTL_CODE(FILE_DEVICE_VIDEO, 295, METHOD_BUFFERED, FILE_ANY_ACCESS);
#endregion

    /// <summary>
    /// Ouvre un handle vers le pAcriphAcrique LCD
    /// </summary>
    /// <returns></returns>
    /// <remarks></remarks>
    private IntPtr OpenLCD()
    {
      //ouverture en lecture partagAce
      return CreateFile("\\\\.\\LCD", GENERIC_READ, SHARE_ALL, IntPtr.Zero, OPEN_EXISTING, 0, IntPtr.Zero);
    }

    private byte[] mBrightness = null;
    /// <summary>
    /// Renvoie la liste des niveaux d'Acclairage supportAcs
    /// </summary>
    /// <value></value>
    /// <returns></returns>
    /// <remarks></remarks>
    public byte[] SupportedBrightness {
      get {
        if (mBrightness == null) {
          int lpRet=0;
          byte[] ret;
          //essaie d'ouvrir le LCD
          IntPtr pDisplay = OpenLCD();
          //si erreur, probablement pas un portable
          if (pDisplay != new IntPtr(-1)) {
              ret = new byte[256];

            //envoie la requAate des niveaux supportAcs
            if (DeviceIoControl(pDisplay, IOCTL_VIDEO_QUERY_SUPPORTED_BRIGHTNESS, IntPtr.Zero, 0, ret, 256, ref lpRet, IntPtr.Zero)) {
              //rAccupA‥re un tableau d'octet contenant les niveaux
                mBrightness = new byte[lpRet];

              for (int i = 0; i < lpRet; i++) {
                mBrightness[i] = ret[i];
              }
            }

            CloseHandle(pDisplay);
          }
        }
        return mBrightness;
      }
    }

    /// <summary>
    /// Renvoie l'Acclairage en cours
    /// </summary>
    /// <returns>Eclairage en cours par modes d'alimentation</returns>
    /// <remarks></remarks>
    public Brightness GetBrightness()
    {
      int lpRet=0;
      Brightness ret = null;
      //essaie d'ouvrir le LCD
      IntPtr pDisplay = OpenLCD();
      //si erreur, probablement pas un portable
      if (pDisplay == new IntPtr(-1))
        return null;

      //envoie la requAate de lecture de l'Acclairage en cours
      DISPLAY_BRIGHTNESS pBrightness = new DISPLAY_BRIGHTNESS();
      if (DeviceIoControl(pDisplay, IOCTL_VIDEO_QUERY_DISPLAY_BRIGHTNESS, IntPtr.Zero, 0, ref pBrightness, Marshal.SizeOf(typeof(DISPLAY_BRIGHTNESS)), ref lpRet, IntPtr.Zero)) {
        //si OK, rAccupA‥re la valeur pour AC/Alimentation et DC/Batterie
        ret = new Brightness();
        ret.AC = pBrightness.ucACBrightness;
        ret.DC = pBrightness.ucDCBrightness;
      }

      CloseHandle(pDisplay);

      return ret;
    }

    /// <summary>
    /// DAcfinit l'Acclairage A  la mAame valeur pour les deux modes d'alimentation
    /// </summary>
    /// <param name="brightness">Niveau d'Acclairage</param>
    /// <returns>true si rAcussi, false sinon</returns>
    /// <remarks></remarks>
    public bool SetBrightness(int brightness)
    {
      return SetBrightness(brightness, brightness);
    }

    /// <summary>
    /// DAcfinit l'Acclairage par mode d'alimentation
    /// </summary>
    /// <param name="acBrightness">Niveau AC/Alimentation</param>
    /// <param name="dcBrightness">Niveau DC/Batterie</param>
    /// <returns>true si rAcussi, false sinon</returns>
    /// <remarks></remarks>
    public bool SetBrightness(int acBrightness, int dcBrightness)
    {
      bool ret;
      int lpRet=0;
      //essaie d'ouvrir le LCD
      IntPtr pDisplay = OpenLCD();
      //si erreur, probablement pas un portable
      if (pDisplay == new IntPtr(-1))
        return false;

      //remplit la structure et informe que l'on dAcfinit les deux valeurs
      DISPLAY_BRIGHTNESS pBrightness = new DISPLAY_BRIGHTNESS();
      pBrightness.ucACBrightness = (byte)(acBrightness & 0xFF);
      pBrightness.ucDCBrightness = (byte)(dcBrightness & 0xFF);
      pBrightness.ucDisplayPolicy = DISPLAYPOLICY_BOTH;

      //envoie la requAate de dAcfinition de l'Acclairage au pAcriphAcrique LCD
      ret = DeviceIoControl(pDisplay, IOCTL_VIDEO_SET_DISPLAY_BRIGHTNESS, ref pBrightness, Marshal.SizeOf(pBrightness), IntPtr.Zero, 0, ref lpRet, IntPtr.Zero);

      CloseHandle(pDisplay);

      return ret;
    }
  }
}

參考原始出處:http://codes-sources.commentcamarche.net/source/view/53617/1259513#browser

Keyword:C#, Windows, Backlight

2014/7/12

【Ubuntu】讓VLC可以播放中文字幕

要讓VLC可以正確顯示中文字幕需要確保兩件事:
  1. 系統有安裝中文字體
  2. VLC選擇正確的字幕編碼格式
  關於第一點,中文字體多數人推薦使用文泉驛正黑體,在Ubuntu系統可以使用以下指令完成安裝:

1
sudo apt-get install ttf-wqy-zenhei

  第二點設定,開啟VLC後點選功能表單的工具編號設定,接著選擇字幕/OSD頁面,將預設編碼設定為正體中文(Big5)並在偏好的字幕語言欄位填入字體路徑:


1
/usr/share/fonts/truetype/wqy/wqy-zenhei.ttc

  做完上述設定重新開啟VLC後應該就可以正確顯示繁體中文字幕了!

2014/7/8

【Ubuntu】設定開機背光值

  在Linux上背光值每次重開機都會被設成最亮,這個問題一直很困擾人,查了一下可以透過修改/sys/class/backlight/acpi_video*/brightness內容來調整背光設定。
底下寫了一個簡單的Script來修改這設定值:


 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
#!/bin/sh

if [ $# -ne 1 ]; then
 echo "Usage: $0 <back light percent>"
 echo "Example: $0 100: Set back light to maximum"
 exit 1
fi

percent=$1

for acpi in /sys/class/backlight/acpi_video*
do
 max=`cat ${acpi}/max_brightness`
 val=$((max*percent/100))
 sudo sed -i "/exit 0$/i echo $val > ${acpi}/brightness" /etc/rc.local
done

使用方式如下:

  • ./set_backlight.sh 100:背光調成最亮
  • ./set_backlight.sh 0:關掉背光

  根據觀察在Notebook上會有兩組設定值,分別是acpi_video0acpi_video1,acpi_video0是插上電源線會參考的數值,而acpi_video1是沒接電源線參考的數值,不過沒找到相關說明文件不是很確認這兩者的區別。

【Ubuntu】開關筆電的觸控板功能

  用Notebook打字最討厭被鍵盤下方的觸控面板干擾,如果筆電觸控板的快捷鍵開關是用Driver控制,這在Linux下就沒法直接用Fn+FunctionKey來作開關了,這時得用到xinput這隻工具來進行控制。
  後來在AskUbuntu討論區有看到別人用script來完成開關切換:執行一次功能就會由關閉變成開啟或是開啟變成關閉,直接看內容:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
#!/bin/bash

declare -i ID
ID=`xinput list | grep -iEo 'TouchPad\s*id\=[0-9]{1,2}' | grep -Eo '[0-9]{1,2}'`
declare -i STATE
STATE=`xinput list-props $ID|grep 'Device Enabled'|awk '{print $4}'`
if [ $STATE -eq 1 ]
then
    xinput disable $ID
    echo "Touchpad disabled."
else
    xinput enable $ID
    echo "Touchpad enabled."
fi

關鍵字:Touchpad、Toggle、Enable、Disable
參考:How to disable the touchpad?

2014/6/11

【樹莓派】(Hidden SSID)無線網路連線

若無線網路環境並沒有取消SSID廣播功能(隱藏SSID),可以參考先前文章設定→http://inpega.blogspot.tw/2013/09/blog-post_15.html

無線網路的SSID是隱藏沒有廣播的話,則可以參考下面設定流程完成連線:
  1. 在命令視窗輸入:wpa_passphrase "SSID" "PASSWORD"

    SSID與PASSWORD需換成實際連線資訊,接著把psk後面推算出來的加密密碼(上圖紅色框起來的資料)記起來。
  2. 接著修改設定文件,sudo nano /etc/network/interfaces
    iface wlan0 inet dhcp
      wpa-scan-ssid 1
      wpa-ap-scan 1
      wpa-key-mgmt WPA-PSK
      wpa-proto RSN WPA
      wpa-pairwise CCMP TKIP
      wpa-group CCMP TKIP
      wpa-ssid
    "SSID"
      wpa-psk d67cff074e68196339ea79313451616edfa4d8c3a3bfd0a64a5ebc06c02fd51d

  3. 紅色框框記得更換成步驟一產生的加密密碼
  4. 接著先關閉wlan功能:sudo ifdown wlan0
  5. 再重新開啟wlan功能:sudo ifup wlan0
  6. 這時可以用指令確認連線狀況:ifconfig wlan0

2014/5/23

【VirtualBox】Windows 8安裝問題

  最近要測試一些系統相容性問題要在VirtaulBox上安裝Windows8卻遇到兩個問題無法安裝。
  首先是安裝畫面帶起來的過程就出問題了,進入藍色的Recovery畫面,Error Code是0xc0000225,畫面如下:
   解決這問題只需開啟虛擬機器設定頁面將晶片組改成ICH9,延伸功能部分把「啟用I/O APIC」選項勾起來即可,截圖如下:
   解掉第一個問題後下個問題馬上出現,這次是停在黑色畫面Error Code是0x0000000C,畫面如下圖:
雖然這問題稍為複雜些,Google上總有神人跳出來解答:這問題是因缺乏CMPXCHG16B指令支援導致。比較麻煩的是這次沒有GUI設定畫面可以勾選解決,必須Key一些命令才行,但指令也很單純:
"c:\Program Files\Oracle\VirtualBox\VBoxManage.exe" setextradata [vmname] VBoxInternal/CPUM/CMPXCHG16B 1
  指令中的[vmname]必須改為自訂的虛擬機器名稱,比如虛擬機器名稱命名為test則上述指令就應該改成:
"c:\Program Files\Oracle\VirtualBox\VBoxManage.exe" setextradata test VBoxInternal/CPUM/CMPXCHG16B 1
  完成上面兩項的修改後應該就可以正確安裝Windows 8了!

2014/4/9

計算Bits中有多少個1

  給定一個二進位數值計算有多少個1在裡面,這是很常見的演算問題也常常在面試中被問到(至少我的面試經驗就曾遇過一次),這個題目就個人觀點來看是驗證受測者是否理解Bit Operator。
  最單純的想法就是每次都檢查最低位元是否為1,是的話統計值就加1,然後把所有位元都往右移一格直到待測值變成0就可以結束了,程式碼如下:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
int count_1_bits_method1 (unsigned int val)
{
 int cnt = 0;
 while (val) {
  if (val & 0x1)
   cnt += 1;
  val >>= 1;
 }
 
 return cnt;
}
需要注意的是參數有用unsigned作為修飾,如果這邊是用有號數作為參數作為參數會導致無窮迴圈,原因在於右移運算子(Right Shift Operator)對於有號值的負數會作負號延展(signed extention)。

  另一個想法則是把最低位元的1依序消除,直到數值裡面都沒有1為止,這步驟做多少次就表示有多少個1,這裡需要巧妙的使用算式N & (N - 1),舉例8(二進位為1000b),減1後會得到7(二進位為0111b),是否觀察到因為最低位減1不夠減了所以依序向前一位數借1,直到最低的高位元是1為止(因為夠借了不需要再往上借位),將1000b與0111b做&(And Bitwise Operator)運算後就可將最低位的1給消除了,程式碼如下:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
int count_1_bits_method2 (int val)
{
 int cnt = 0;
 while (val) {
  cnt += 1;
  val = val & (val - 1);
 }
 
 return cnt;
}

  兩個演算方法最差的狀況所需時間都一樣,但平均而言第二種效率會好一些,會出這題目主要是想確定受測者能靈活運用位元運算子吧!?面試系統底層或是嵌入式相關工作應該會比較容易遇到這類考題。