2014/12/31

【C#】製作不規則的視窗

  在C#中要建立不規則視窗(Irregularly Shaped Window)非常簡單,只需要準備一張欲顯示形狀的底圖並修改幾個視窗屬性(Window Properties)即可完成:
  1. 設定FormBorderStyleNone

    可透過設定頁面完成設定或是直接撰寫程式
    FormBorderStyle = System.Windows.Forms.FormBorderStyle.None;
    
  2. 指定BackgroundImage並依據需求設定BackgroundImageLayout
  3. 設定TransparentKey為底圖不要顯示部分的顏色,若使用範例圖片則設定為白色
  4. 到目前為止已經完成客製化的視窗介面,編譯並執行可以看到視窗呈現愛心形狀,心型以外區域可以顯示桌面內容,點選心型以外的區域也可不受限制
  因為一開始我們把FormBorderStyle設定為None,邊框都不顯示連帶的最上面的標題列與控制盒(最小、縮小、最大化按鈕)也一併被取消了,因此若需要相關控制必須自行增加相關按鈕實現,底下示範移動視窗功能:
  1. 切換到檢視程式碼視窗,在視窗類別中增加變數
    private Point mouse_offset;
    
  2. 增加MouseDown事件
    private void Form1_MouseDown(object sender, MouseEventArgs e)
    {
        mouse_offset = new Point(-e.X, -e.Y);
    }
    
  3. 增加MouseMove事件
    private void Form1_MouseMove(object sender, MouseEventArgs e)
    {
        if (e.Button == MouseButtons.Left)
        {
            Point mousePos = Control.MousePosition;
            mousePos.Offset(mouse_offset.X, mouse_offset.Y);
            Location = mousePos;
        }
    }
    
  4. 若不是透過屬性視窗新增事件,記得自己手動加入事件註冊
    this.MouseDown += new System.Windows.Forms.MouseEventHandler(this.Form1_MouseDown);
    this.MouseMove += new System.Windows.Forms.MouseEventHandler(this.Form1_MouseMove);
    
  完成上述步驟後,此視窗就可以透過滑鼠左鍵拖曳進行移動了!

範例用圖:

2014/11/6

國父紀念館

  國父紀念館可說是陸客到台北市的必訪景點之一,從國父紀念館可以直接看到101大樓,兩個景點非常靠近,很多大陸團行程應該是早上到國父紀念館參觀接著就到101吃鼎泰豐到觀景台遠眺台北市景吧!

  國父紀念館外的走廊常常會被學生團體用來排練活動。

到國父紀念館參訪重要行程就是瞻仰國父雕像,跟中正紀念堂一樣雕像兩旁會有儀隊站崗,整點一到會有交接表演活動,很多遊客會在20分鐘前就先來卡位等候表演,若想觀賞表演的話可不能等到整點才到。

面對國父雕像右邊進去是東室,主要陳列國父生平相關史蹟,一進去會看到整片牆的建國大綱十分壯觀。

天花板則以國徽裝飾,館內有志工提供導覽講解。

面對國父雕像左側為西室,主要介紹國父提倡的三民主義思維與民主體驗,也有志工可做導覽解說。

西室有設置模擬投票所,可以體驗投票的感覺。

另外值得一提的是,國父紀念館也常是台灣影視娛樂三大獎(金馬獎、金鐘獎與金曲獎)的頒獎地點,時間來對的話說不定還可看到繽紛絢麗的星光大道!

2014/10/17

中正紀念堂

  中正紀念堂跟兩廳院(國家戲劇院和國家音樂廳)一起組成一個園區,在地人多半是專程來看展覽或藝文演出比較多,近年開放陸客觀光中正紀念堂因兩岸之間的歷史因素成了陸客來台的必訪景點之一。
遊客到中正紀念堂參訪重點大略有三:
  第一不外乎爬上階梯到最上層瞻仰蔣總統銅像,並且跟兩旁的儀隊合影。
  其中天花板的藻井設計並嵌入國徽也是很具特色的設計。
第二就是到一樓參觀文物陳列,從蔣總統用過的家具、衣著到行書公文都可以一窺究竟,連當年的總統座車都被收藏在此展覽。
第三個重點則是等到整點時到銅像廳堂觀賞儀隊的交接表演,儀隊從上午九點到下午五點每小時都會有交接儀式,很多人會把這表演視為旅遊重點呢!

  巧思的設計:
  紀念堂走上去到銅像大廳的階梯總共是89階,這數字表示的是蔣總統89歲高壽。
兩廳院分別坐落在中正紀念堂對出去的左右兩側,其形狀從天空俯瞰恰好是一個雙十的示意,表示中華民國的建國紀念日(10月10日)。
園區還有一個重要地標:自由廣場,當年野百合學運的發生地點,在台灣歷史上占據重要的篇章。

2014/8/20

高空彈跳(Bungy Jumping)

今年五月原本只是跟朋友約好去桃園兩日遊,不小心就加了一個高空彈跳行程,原本想說作個好咖跟大家一起報名,結果當天才知道只有三分之一的人報名。。。頭都洗一半了只好硬著頭皮排隊跳樓(不對,是跳橋。其實是因為錢都繳了,不跳實在很浪費)

一到大漢橋,往下一看不妙!跟想像的完全不一樣,溪裡沒什麼水,石頭倒是很多,如果一個不小心應該就是七天後回家了耶。去之前還在想上半身會不會進到水裡之類的,看來都是多想的。。。
在桃園大漢溪跳的高度是72公尺,一樓3公尺來算大概就是24樓高!

教練在把人推下去之前會先幫忙綁好繩子並作扣環的檢查(這是廢話),沒問題後就會要你站到橋邊上頭,跳之前會再一次口頭確認心理狀態準備好沒,當天有人太緊張被請下來(所以除非說準備好了,不然應該是不會硬推)。

三。二。一倒數完就是個人武術天份了,可以像這樣大鹏展翅帥帥的。。。掉下去

也可以像這樣緊握拳頭任由地心引力快速把人給吸下去

整個過程其實滿舒服的,有幾點心得:

  1. 人生跑馬燈其實沒有出現
  2. 真正讓人冒冷汗的時間點是在第一次彈起來又往下那一瞬間(因為心理沒預期會有第二個自由落體)
  3. 男人的第二個生命體並沒有傳說中的痛
  4. 鞋要穿緊,或是赤腳(同行友人就把剛買一個月的新鞋送給河神了,但這裡的河神沒這麼勤勞上來問你掉的是臭鞋還是金鞋,所以誠實的人也沒辦法拿回鞋子)
在下面擺盪一陣子後,教練會放一條鋼索下來,自己扣好後就會慢慢的被拉上來,爬上橋後就完成一次高空彈跳啦!

最後會領到一個挑戰證書,跟一張會員卡(下次跳有優惠),這次打卡有多送一個手環。還滿不錯的紀念!

2014/8/18

【Python】print不換行作法

網路上網路上Google:python print no new line,可以得到很多的討論串,多數會將Case分成python3與先前版本兩種來解決這問題,先看看這兩種作法(最後探討各版本間相容的作法):
  1. python3的作法
    1
    2
    print('Hello World', end='')
    print(', Same Line')
    
  2. 早期版本的作法
    1
    2
    print('Hello World'),
    print(', Same Line')
    
上面二分法的解決方法在能確保選定python版本後就不會作修改的情況下,一切都能運作正常不會發生"意外"。但實際上,在程式開發中很多臭蟲都是在這一念之間埋下惡果的!

在知道有版本相容性問題時,最好方法還是得找個兩全其美的方案會好些,參考下面思緒決定第三種解決方案:
  • 查閱手冊發現2.1版以後增加了Lib/__future__.py模組:在遇到換行的問題上可透過引入此模組的print_function讓2.1到2.X版都能使用python3的解法。
  • 但如上述所說必須2.1以後版本才支援此模組,稍微查了一下python2.1大約是2001年finally release的,掐指一算離現在大概也13年了,再查看手邊系統環境python版本是2.7。
  • 考量上述兩點,遇到2.1以前的版本機率應該遠小於之後更新python版本到3.X甚至更之後版本,因此採用第三種解決方法應該會是比較好的選擇。
1
2
3
4
from __future__ import print_function

print('Hello World', end='')
print(', Same Line')

2014/8/6

巴里島‧人

  前陣子聽了關於旅遊與攝影的演講,體認到旅行中文化體驗很重要的一環就是與當地人有互動,這次到巴里島便試著開口與當地人有些互動,確實讓旅行增添了很多樂趣與回憶!

  下面這張照片是在象洞(Goa Gajah)遇到的小兄妹,他們正一起享用午餐?鼓起勇氣說了Hello,回了我靦腆的笑容,接著用按快門手勢比著手機說Can I?小弟弟點點頭舉起了YA擺了個Pose給我拍。

  把手機的照片拿給他們欣賞,小妹妹接著拿起湯匙放在嘴巴,眼睛變看著鏡頭,顯然很有做Model的天分!

  下面這張照片是在Jatiluwih梯田(Jatiluwih Rice Terrace)拍的,這裡的人滿有趣的,看到你有拿拍照機器只要彼此有Eye Contact後,他們就會擺一個帥氣的姿勢定格,等待你按下快門,彼此Hello打個招呼發現他們也不太能說英文後就用笑容結束了這短暫的交流囉。

跟這YES小女孩互動的過程很有趣。
我:Hello, Can I have a picture for you?
小女孩:Yes!
說完擺了個YA讓我拍完照片。
我:You are so beautiful. What's your name?
小女孩:Yes!
我(揮手狀):Thank you, bye bye!
......好吧,原來我們剛剛成功完成拍照交流,是透過肢體語言成功的,我想因為這梯田已經成為重要觀光景點,大人可能教他們回應外來遊客都說Yes吧!

  在準備離開梯田前,不小心和這位阿伯Eye Contact到了,立馬拿起鐮刀擺起這帥氣姿勢,這下不拍可就尷尬了,所以有了下面這張照片。

  在塔曼阿雲寺(Pura Taman Ayun)裡有個亭子,有兩位畫家正在進行創作,原本看他們很專注不好意思上前打擾,但他們的畫像其實很漂亮,外面繞了又繞決定上前問問能不能拍照,得到的回應是:Of course。這次應該是真的用語言完成交流了吧!

  在水神廟遇到放風箏(很厲害,他們自己做的)的小男生們,一樣用手比著手機作勢拍照樣:Can I?得到了一個鬼臉定格。

友人問Can I?但沒做出拍照手勢,得到了這個No的鬼臉,猜想應該是以為要借風箏玩吧!

  這小鬼很皮,在拍照過程一直用風箏線要把我頭上帽子勾下來。

  這張是在飯店Check in時拍的,問他能不能拍,用滿標準英文回應:Sure。但臉上似乎有一點點的無奈感,當時是晚上八點左右。隔天早上七點多到大廳吃早餐發現他已經在那邊咚咚咚,終於明白他那臉上的疲累感是怎麼來的......

  在巴里島還能看到很多東南亞國家人民必備技能:頭頂工夫。這技能實在是一絕,而且不論男女老少都具備,連我們司機都很得意的說我也會喔!


  最後我決定把這兩隻猴子也歸為人,看看這猴子在懸崖邊惆悵的望著印度洋,眼神深的像是裝進了全世界的憂愁般,都想把張老師熱線抄給牠了。
  地點:烏魯瓦圖懸崖廟(Pura Luhur Uluwatu)。

  這隻坐在垃圾桶旁的也是,像是吞進多少的委屈悶在心頭似的。

【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;
}

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

2014/2/23

【C/C++】修飾子小細節大臭蟲

這裡要探討的是修飾子可能導致的臭蟲,很容易埋入的臭蟲而且難以找出,直接看下面這段程式:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
#include <stdio.h>
#include <linux/types.h>

int main (int argc, char *argv[])
{
 __u16 b;
 __u8 a[3];
 
 b = 102;
 
 if (argc <= 1) {
  printf ("Please provide 1 integer as arguments\n");
  return -1;
 }
 
 if (sscanf (argv[1], "%d", &a[0]) != 1) {
  printf ("Wrong argument: %s\n", argv[1]);
  return -1;
 }
 
 printf ("a[0] = %d, b = %d\n", a[0], b);
}

先看執行結果:

1
2
$ ./test 10
a[0] = 10, b = 0

從程式描述可知預期輸出結果應該是b = 102
但問題在哪呢?如果有試著編譯上述程式碼還會發現編譯時,編譯器早發覺不對勁而提出警告了:

1
2
3
$ gcc -o test test.c 
test.c: In function ‘main’:
test.c:16:2: warning: format ‘%d’ expects argument of type ‘int *’, but argument 3 has type ‘__u8 *’ [-Wformat]

根據編譯器提出的警告,可以有兩種修正方法:
第一種,讓編譯器開心但會徹底把臭蟲埋入程式且難以發現:
既然編譯器預期%d修飾符應該給對應一個int *類別變數,那就改成

1
2
3
4
if (sscanf (argv[1], "%d", (int *)&a[0]) != 1) {
 printf ("Wrong argument: %s\n", argv[1]);
 return -1;
}

改完後看來編譯器滿意了,沒有丟出任何警告執行應該就沒問題吧,結果發現結局一樣,這時不禁開始懷疑sscanf這個函數是不是有bug阿?

翻閱sscanf手冊的Conversions區段有以下描述:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
Conversions
  The following type modifier characters can appear in a conversion spec‐
  ification:

  h      Indicates that the conversion will be one of d, i, o, u,  x,  X,
         or  n  and  the  next  pointer  is  a  pointer to a short int or
         unsigned short int (rather than int).

  hh     As for h, but the next pointer is a pointer to a signed char  or
         unsigned char.

還沒看出端倪嗎?沒關係,再看看下面增加幾行印出變數位址的程式內容:

 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
#include <stdio.h>
#include <linux/types.h>

int main (int argc, char *argv[])
{
 __u16 b;
 __u8 a[3];
 
 b = 102;
 
 if (argc <= 1) {
  printf ("Please provide 1 integer as arguments\n");
  return -1;
 }
 
 if (sscanf (argv[1], "%d", (int *)&a[0]) != 1) {
  printf ("Wrong argument: %s\n", argv[1]);
  return -1;
 }
 
 printf ("a[0] = %d, b = %d\n", a[0], b);
 printf ("Address of a[0]: %p\n", &a[0]);
 printf ("Address of b: %p\n", &b);
 printf ("Size of int: %d\n", sizeof (int));
}

執行結果如下:

1
2
3
4
5
$ ./test 10
a[0] = 10, b = 0
Address of a[0]: 0xbfc058cb
Address of b: 0xbfc058ce
Size of int: 4

發現變數b距離a[0]只有3 bytes,且得知在此系統環境下一個int長度是4 bytes,此時是否恍然大悟?原來sscanf使用%d修飾字要求把argv[1]字串解析成數值為int並且存入a[0],但a[0]實際只有1 bytes呢!多出來的3 bytes就把記憶體後面連續的空間也改蓋掉了,此時變數b的內容就給填為0了。

第二種修正方法:
回憶剛剛貼的手冊說明內容,既然後面承接的變數長度為8 bits,則應該將程式內容改為:

1
2
3
4
if (sscanf (argv[1], "%hhd", &a[0]) != 1) {
 printf ("Wrong argument: %s\n", argv[1]);
 return -1;
}

得到結果也符合預期且編譯器也不再哇哇叫,皆大歡喜!

Keywords: C/C++、Variable Alignment

2014/2/9

【Ubuntu】Chromium瀏覽器播放影音串流檔

因為各種影音解碼器(codecs)有版權問題,瀏覽器預設沒有帶入播放串流影音(如:mp3 stream)功能,如果需要相關串流影音必須自行另外安裝:

sudo apt-get install chromium-codecs-ffmpeg-extra
安裝完後重新啟動瀏覽器(有可能需要重新開機)就可以正常播放串流影音了。

Key Word:ubuntu, browser, chromium, stream codecs