飴屋ぷろじぇくと

 ちょっとカテゴリを分けてみようかなっと。

--.--.--[--] スポンサーサイト

上記の広告は1ヶ月以上更新のないブログに表示されています。
新しい記事を書く事で広告が消せます。

2014.11.17[月] xlibでデスクトップに立つ。

突然ですが、xlibというのはX Window Systemの基本的なクライアントライブラリです。
X Window SystemというのはまあUNIX系のウィンドウシステムはほぼ全部これが使われてたりする、Linux使いの人なら常識ともいえるウィンドウシステム、というかプロトコルなんですが。

と、前提知識をお知らせしたところで、今回のお題は、
StandOnDesktop.png
これです。
Xといいつつ、Mac OSXですが。
OSXはMac独自のウィンドウシステムでXは使ってないんですが、X11.appというアプリが入っていて(バージョンによってはオプション)Xウィンドウアプリを実行出来るようになっています。

まあそれはそれとして、本題のxlibプログラミングです。
今時はGTK+やらQtやらが良くこなれていて、xlibを直接使うなんて時代遅れもいい所なんですが、数年、いや十年位前か、Linuxでデスクトップに立たせるには?を調べまくって結局よく判らなくて苦渋をなめた経験がありまして。
今回、「マルチプラットフォームでウィンドウアプリを作るにはどんな選択肢があるか」を調べる一環で、Xウィンドウプログラミングも引っかかりまして、「そういやこんなの昔調べたな」とか思っていたらあれからネット上の情報も増えていて今度は比較的容易に情報が集まり実際立たせる事が出来てしまった(上の画像参照)、というわけ。以下プログラミングな話題。
1.ウィンドウを開くには
xlibプログラミングの基本です。
#include <stdio.h>
#include <X11/Xlib.h>

main()
{
Display *dsp;
Window win;
dsp = XOpenDisplay( NULL );
win = XCreateSimpleWindow( dsp, DefaultRootWindow(dsp),
0, 0, 400, 300, 1,
BlackPixel(dsp,0), WhitePixel(dsp,0) );
XMapWindow( dsp, win );
XFlush( dsp );
getchar();
XCloseDisplay( dsp );
}
最低限です。イベントループもgetchar()で代用。
コンパイルはこんな風にします
gcc -o maru maru.c -I /usr/X11R6/include -L /usr/X11R6/lib -lx11
2.四角くないウィンドウを作るには(赤が追加部分)
これが大切です。10年前はどう探しても無駄に複雑なコードに翻弄されるばかりで要点を突いたシンプルな例が全然見つかりませんでした。
#include <X11/Xlib.h>
#include <X11/extensions/shape.h>
#include <stdlib.h>
char *maru_shape( int r) /* 丸いマスクを作る */
{
int i, j;
int r2 = r * 2;
int rd = r * r;
int w = (r2 + 7) / 8;
char *ret = (char*)calloc(1, r2 * w);
for (i = 0; i < r2; i++) {
int m = i * w;
int l1 = (i - r) ; l1 *= l1;
for (j = 0; j < r2; j++) {
int l2 = (j - r) ; l2 *= l2;
if (l1 + l2 <= rd) {
ret[m + j / 8] |= 1 << (j % 8);
}
}
}
return ret;
}

int main()
{
Display *dsp;
Window win;
char *mask;
Pixmap pm;

dsp = XOpenDisplay( NULL );
win = XCreateSimpleWindow( dsp, RootWindow(dsp, 0),
0, 0, 200, 200, 0,
BlackPixel(dsp,0), WhitePixel(dsp,0) );
mask = maru_shape(100);
pm = XCreateBitmapFromData( dsp, RootWindow(dsp,0), mask, 200, 200);
free(mask);
XShapeCombineMask( dsp, win, ShapeBounding, 0, 0, pm, ShapeSet);


XMapWindow( dsp, win );
XFlush( dsp );
getchar();
XCloseDisplay( dsp );
}
maru1.png
まずメモリ上に1ピクセル1ビットのビットマップをつくり、そこからXCreateBitmapFromData()でウィンドウの形状に適用するためのPixmapを作り、それをXShapeCombineMask()でウィンドウに適用します。
コンパイルはこんな風。エクステンションライブラリを使う指定をしてます。
gcc -o maru maru.c -I /usr/X11R6/include -L /usr/X11R6/lib -lx11 -lXext
でもタイトルバーはいらないですよね。
3.タイトルバーの無いウィンドウを作るには
こんなだそうです。
void change_prop(Display *display, Window window)
{
#define MWM_HINTS_FUNCTIONS (1L << 0)
#define MWM_HINTS_DECORATIONS (1L << 1)
#define MWM_HINTS_INPUT_MODE (1L << 2)
#define MWM_FUNC_MOVE (1L << 2)
#define MWM_INPUT_MODELESS 0
struct {
long flags;
long functions;
long decorations;
long inputmode;
} prop;
prop.flags=MWM_HINTS_FUNCTIONS|MWM_HINTS_DECORATIONS|MWM_HINTS_INPUT_MODE;
prop.decorations=0;
prop.functions=MWM_FUNC_MOVE;
prop.inputmode=MWM_INPUT_MODELESS;
Atom a=XInternAtom(display,"_MOTIF_WM_HINTS",0);
XChangeProperty( display, window, a, a, 32, PropModeReplace, (unsigned char*)&prop, 4);
}
maru2.png
ウィンドウ生成後にDisplayとタイトルバーを消したいWindowを渡してやるとそのように設定してくれます。いかにもコピペ然としてますが、その通り拾って来たコードをコピペです。公式のマニュアル読んでもさっぱり判らなかったので。
でもお月様なら上でも良いですが、目的は「デスクトップに立たせる」ことですので、下に張り付かなくてはいけません。小技ですが、
4.画面のサイズを取得するには
Display *dpy = XOpenDisplay(NULL);
int screen = DefaultScreen(dpy);
int width = DisplayWidth(dpy, screen);
int height = DisplayHeight(dpy, screen);
5.ウィンドウをスクリーン上の任意の位置に移動するには
XMoveWindow( display, window, x, y );
6.ウィンドウの位置とサイズを同時に変更するには
XMoveResizeWindow(display, window, x, y, width, height);
↑こんな便利なのがありました。こっちを使いましょう。まだまんまるですが、このウィンドウは画像のサイズに合わせる必要がありますから
	int screen, width, height;

dsp = XOpenDisplay( NULL );
screen = DefaultScreen(dsp);
width = DisplayWidth(dsp, screen);
height = DisplayHeight(dsp, screen);

win = XCreateSimpleWindow( dsp, RootWindow(dsp, 0),
0, 0, 200, 200, 0,
BlackPixel(dsp,0), WhitePixel(dsp,0) );
(…中略…)
/* 表示 */
XMapWindow( dsp, win );
/* デスクトップに立つ */
XMoveResizeWindow( dsp, win 0, height - 200, 200, 200);

こうやって表示後に移動します。
maru3.png
ドックの後ろに行ってしまいましたが、これは下に張り付いた事を示すためで、普段は自動でドックを引っ込ませてます。
どんどん行きます。
7.イメージファイルを読み込んで表示するには
Windowsの24bitカラーのBMPファイルを読み込みます。その方が簡単だからです。出来てしまえば更にPNGとかに対応するのも容易い筈です。
/* BMPファイルを読みこんで画像データなどを返します。*/
char *loadbmpfile(const char *filename, int *width, int *height, int *bitcount, int *linelen)
{
char *bmpdata;
int i, j, k, size;
int off, hs, w = 0, h = 0, bcnt = 0;
int line;
int pl = 0, bicomp = 0, clrused;
char bm[3];
FILE *fi = fopen(filename,"rb");
bcnt = 0;
if (fi) {
fread(bm, 1, 2, fi);
fread(&i , 4, 1, fi);//fsize
fread(&i , 2, 2, fi);
fread(&off , 4, 1, fi);//offset
fread(&hs , 4, 1, fi);//hdsize
if (hs == 40) {
fread(&w , 4, 1, fi);//width
fread(&h , 4, 1, fi);//height
fread(&pl , 2, 1, fi);//planes
fread(&bcnt , 2, 1, fi);//bitcount
fread(&bicomp, 4, 1, fi);//biComp
fread(&i , 4, 1, fi);//biSizeImage
fread(&i , 4, 1, fi);//biXPixPerMeter
fread(&i , 4, 1, fi);//biYPixPerMeter
fread(&clrused , 4, 1, fi);//biClrUsed
fread(&i , 4, 1, fi);//biClrImpotant
} else {
fread(&w , 2, 1, fi);//width
fread(&h , 2, 1, fi);//height
fread(&pl , 2, 1, fi);//planes
fread(&bcnt , 2, 1, fi);//bitcount
}
line = w * (bcnt / 8);
line = ((line + 3) / 4) * 4;
size = line * h;
if (size < 0) size = -size;
bmpdata = (char*)malloc(size);
fread(bmpdata, 1, size, fi);

fclose(fi);
}
*bitcount = bcnt;
*width = w;
*height = h;
*linelen = line;
return bmpdata;
}
/* BMPファイルを読みこんでXImageを返す */
XImage *load_bmp(Display *d, const char *filename, int *imwidth, int *imheight) {
char *bmpdata;
int scrn = DefaultScreen(d);
int depth = XDefaultDepth(d, 0);
Colormap cmap = DefaultColormap(d, scrn);
int width, height, bitcount, linelen, bpp;
XImage *img;
char *data;
XColor color;
int i, j, h;
bmpdata = loadbmpfile(filename, &width, &height, &bitcount, &linelen);
bpp = depth >= 24 ? 4 : 2;
h = height;
if (height < 0) height = -height;
data = (char*)malloc(height * width * bpp);
for (i = 0; i < height; i++) {
int k = h < 0 ? i * linelen : (height - i -1) * linelen;
int m = i * width * bpp;
for (j = 0; j < width; j++) {
int r = k + j*(bitcount/8);
int s = m + j * bpp;
memcpy(data+s, bmpdata+r, bpp);
}
}
free(bmpdata);
img = XCreateImage( d, CopyFromParent, depth, ZPixmap,
0, data, width, height, bpp*8, bpp*width );
*imwidth = width;
*imheight = height;
return img;
}
このコードはあまり推敲されてませんので違う環境で作ったBMPファイルだと読めない可能性があります。あしからず。
そして、こいつをウィンドウに適用するには、
int main()
{
Display *dsp;
Window win;
char *mask;
Pixmap pm;
int screen, width, height;
int iwidth, iheight;
GC gc;
XImage *image;

/* ディスプレイ */
dsp = XOpenDisplay( NULL );
/* イメージを読み込む */
image = load_bmp(dsp, "defoko.bmp", &iwidth, &iheight);


(…中略…)

/* 表示 */
XMapWindow( dsp, win );
/* デスクトップに立つ */
XMoveResizeWindow( dsp, win, 0, height - 200, 200, 200);
/* グラフックコンテクスト */
gc = XCreateGC(dsp, win, 0, 0);
/* イベントマスク */
XSelectInput( dsp, win, ExposureMask);
/* アプリのイベントループ */
while (1) {
XNextEvent(dsp, &event );
switch(event.type) {
case Expose: /* 再描画など */
if (event.xexpose.count == 0) {
/* ここで再描画の度にイメージを描画する */
XPutImage(dsp, win, gc, image, 0, 0, 0, 0, iwidth, iheight );
}
break;
}
}

XCloseDisplay( dsp ); //ここには来ない。
}
maru4.png
これは……。丸の形状が残ってて妙な具合になってますね。
これはこれで趣がありますが、もう一息です。
8.画像の形状をしたウィンドウをつくるには
イメージからマスクを作成してウィンドウをその形にします。まずはこんな関数でメモリ上にビットパターンを生成。
/* 1pixel=1byte のマスクを作成します */
char *make_shape( XImage *img )
{
char *data = img->data;
int bpp = img->bitmap_pad / 8;
int size = (img->width + 7) / 8 * img->height;
int width = img->width;
int height = img->height;
int i, j;
char * ret;
ret = malloc(size);
if (ret == NULL) {
fprintf( stderr, "cannot malloc\n" );
exit( 1 );
}
memset( ret, 0, size );
for( i = 0; i < height; i++ ){
for( j = 0; j < width; j++ ){
int m = i * width + j;
if (memcmp(data, data + m * bpp, bpp)) { /* 左上端のピクセルを透過色にします */
ret[m / 8] |= 1 << (m % 8);
}
}
}
return ret;
}
そして、メイン関数はここを変更します。
	/* イメージからマスクを作り、ウィンドウの形に設定 */
mask = make_shape(image);
pm = XCreateBitmapFromData( dsp, RootWindow(dsp,0), mask, iwidth, iheight);
free(mask);
XShapeCombineMask( dsp, win, ShapeBounding, 0, 0, pm, ShapeSet);
maru5.png
出来ました! 見事にデスクトップに立ってくれました。
お疲れさまです。

Comment






(編集・削除用)

Trackback

http://utau2009.blog114.fc2.com/tb.php/31-6d55b3fd

この記事にトラックバック(FC2Blog User)

Copyright © 飴屋/菖蒲

上記広告は1ヶ月以上更新のないブログに表示されています。新しい記事を書くことで広告を消せます。