2009年03月04日

GTK+SDLでタスクトレーで管理するJoyMouse

SDLでJoystickの取得
xlibでマウスイベントの送信
GTKでタスクトレーの管理ができるようになったので
まとめてタスクトレーで終了できるジョイマウスを作ってみることにした。
まだ不完全だが基本要素は揃っているので全ソースも載せた。
気になった問題はジョイステッィクのループとGTKのループのバッティングで
選択肢としては

  1. ジョイスティックデーモンを起動してプロセスをkillする。

  2. 別スレッドでSDLを回す

  3. タイマーで定間隔で呼び出す

  4. アイドルタイムを使う


ということだったのだが

GTKではなくてglibの方に
タイマーをセットするg_timeout_addと
アイドルタイムの作業を指定するg_idle_addがあった。

どちらもg_booleanを返り値としていて
return 1だと繰り返し呼び出し
return 0だとそれで終了なので使いやすかった。

今回はだらだらとグローバル変数を使ってアイドルの方でやった。

ビルドのためのコマンドは

gcc -O2 -o gtk_007 gtk_007.c \
`pkg-config gtk+-2.0 --cflags ` \
-I/usr/local/include/SDL -I/usr/local/include\
-pthread -L/usr/local/lib -lgtk-x11-2.0 \
-lSDL -lXtst

いろいろ試したらGTKのビルドではincludeパスは必要だけれども
ライブラリーはgtk-x11-2.0だけ指定すれば良いことが分かった。

ヘッダー

#include <gtk/gtk.h>
#include <X11/Xlib.h>
#include <SDL.h>


グローバル変数

GtkStatusIcon *sys_t;//システムトレイ
static gboolean param = TRUE;
char* str[3]={"JoyMouse有効","JoyMouse無効","hello system tray"};
char* icos[10]={GTK_STOCK_NO,GTK_STOCK_NETWORK,GTK_STOCK_MEDIA_PLAY,
GTK_STOCK_INFO,GTK_STOCK_ABOUT,GTK_STOCK_APPLY,GTK_STOCK_CANCEL,
GTK_STOCK_CLOSE,GTK_STOCK_DIALOG_AUTHENTICATION,GTK_STOCK_DIALOG_ERROR};
//ストックアイコン

int cnt=1;
int flg,jid;
static gboolean isBlinking = TRUE;
//
Display *dpy;//X通信用
SDL_Joystick *joystick;//ジョイスティック
int mbut,sft;
int x, y, h;
int btns[16];


charの変数がが多いのは実験だから


gboolean LookJoy (gpointer data)
{
//var

SDL_Event s_event;
int i,xw,yw;
//begen
//溜まったイベントの処理
while ( SDL_PollEvent(&s_event) ) {
switch (s_event.type) {
case SDL_JOYAXISMOTION:
if (s_event.jaxis.axis){y=s_event.jaxis.value;
} else {x=s_event.jaxis.value;}
break;
case SDL_JOYHATMOTION:
h=s_event.jhat.value;
break;

case SDL_JOYBUTTONDOWN:
i=s_event.jbutton.button;
btns[i]=1;
i++;
//ボタンは1から3までのみなので
if (i<4){XTestFakeButtonEvent(dpy, i,True, CurrentTime);}
break;
case SDL_JOYBUTTONUP:
i=s_event.jbutton.button;
btns[i]=0;
i++;
if (i<4){XTestFakeButtonEvent(dpy, i,False, CurrentTime);}

break;
case SDL_QUIT:
break;
default:
break;
}
}
//1024x768きめうち、要改造部分
xw=(int)(x*512 /30000+512);
yw=(int)(368-y*384 /30000);

//はまると再起不能になるのでラストキーを押しているときだけマウスが動く
if (btns[mbut]){
XTestFakeMotionEvent(dpy, -1, xw, yw, CurrentTime);
}
XFlush(dpy);//これがないと反映しない。
SDL_Delay( 10 );//安全策
return flg;//繰り返し
}

xとyはばらでくるのでローカル変数にするとリセットされてマウスがふらつく
ディスプレイ解像度が決め打ちだしボタンの割り当てもできないが
これが基本。
ハットやデジタルジョイスティック用の事をまだ考えていない。


void on_sys_t_activate(GtkStatusIcon *status_icon, gpointer user_data)
{
gtk_status_icon_set_from_stock(status_icon,icos[cnt]);
gtk_status_icon_set_tooltip(status_icon, icos[cnt]);
cnt++;
if (cnt==10){cnt=0;};
}

クリック用関数アイコンとメッセージが順々に変わる。
リソースはシステムが用意しているものでcharで指定定数化されているようだ。
まだ調査不足、とりあえず自前で用意しなくてもgnomeかxfceのアイコンライブラリーが
入っていればマウスとかのアイコンもありそう。


void
on_item1_activate (GtkMenuItem *menuitem,
gpointer status_icon)
{
flg = !flg;
if (flg){g_idle_add(&LookJoy,NULL);}
}

void
on_item2_activate (GtkMenuItem *menuitem,
gpointer user_data)
{
gtk_main_quit();
}


今回はメニューにもてを出してみた。これはメニュー用のコールバック
メニューで選ぶとジョイマウスが有効になり無効にもなる。
今回はコールバックにディレイをつけたのでアイドルバージョン


GtkWidget*
create_menu (GObject *status_icon)
{
GtkWidget *menu;
GtkWidget *item1;
GtkWidget *item2;

menu = gtk_menu_new ();
gtk_widget_show (menu);

item1 = gtk_menu_item_new_with_mnemonic (str[flg]);
gtk_widget_show (item1);
gtk_container_add (GTK_CONTAINER (menu), item1);

item2 = gtk_menu_item_new_with_mnemonic ("Quit(_Q)");
gtk_widget_show (item2);
gtk_container_add (GTK_CONTAINER (menu), item2);

g_signal_connect ((gpointer) item1, "activate",
G_CALLBACK (on_item1_activate),
status_icon);

g_signal_connect ((gpointer) item2, "activate",
G_CALLBACK (on_item2_activate),
NULL);
return menu;
}

メニューの生成、flgで文字を変えている。
タスクバーのメニューは結構操作し辛い。

右クリック用関数

void on_sys_t_popup(GtkStatusIcon *status_icon, guint button,
guint activate_time,gpointer user_data)
{
GtkWidget *menu;
menu = create_menu(G_OBJECT(status_icon));
GdkEvent *event;

event = gdk_event_new(GDK_3BUTTON_PRESS);
guint32 event_time = gdk_event_get_time(event);

gtk_menu_popup (menu, NULL, NULL, gtk_status_icon_position_menu, status_icon, 2, event_time);

gdk_event_free(event);
}

システムトレー右クリック時のメニュー表示部分。パラメータはまだよくわかっていない。


int main(int argc, char *argv[])
{

//var
//意味がよく分からないが最初にやってみた。
gtk_init(&argc, &argv);

char *dname = NULL;
//begen
//Xとの通信
if (!(dpy = XOpenDisplay(dname))) {
fprintf(stderr, "Can't Open Display.\n");
exit(1);
}
//SDLイニシャライズ
if ( SDL_Init(SDL_INIT_VIDEO|SDL_INIT_JOYSTICK) < 0 ) {
fprintf(stderr, "Couldn't initialize SDL: %s\n",SDL_GetError());
exit(1);
}
//引数があればそのジョイスティックを指定
if ( argv[1] ) {
joystick = SDL_JoystickOpen(atoi(argv[1]));
} else {
joystick = SDL_JoystickOpen(0);}

//開けなかった場合
if ( joystick == NULL ) {
printf("Couldn't open joystick %d: %s\n", atoi(argv[1]),
SDL_GetError());}
else{
//
//最大ボタン数
mbut=SDL_JoystickNumButtons(joystick);mbut--;

//一応スペックなんぞを表示、ターミナル起動向け
printf("\n %d joysticks attached\n", SDL_NumJoysticks());
printf("%d axes, %d hats, %d balls, and %d buttons\n",
SDL_JoystickNumAxes(joystick), SDL_JoystickNumHats(joystick),
SDL_JoystickNumBalls(joystick),SDL_JoystickNumButtons(joystick));

//ストックアイコンからシステムトレイを作る。
sys_t = gtk_status_icon_new_from_stock(GTK_STOCK_NO);


gtk_status_icon_set_tooltip(sys_t, str[2]);

g_signal_connect(G_OBJECT(sys_t), "activate",
G_CALLBACK(on_sys_t_activate), NULL);
g_signal_connect(G_OBJECT(sys_t), "popup-menu",
G_CALLBACK(on_sys_t_popup), NULL);

//実験の残骸 タイマーの場合とアイドルの場合
//g_idle_add(&LookJoy,NULL);
//jid=g_timeout_add(33,&LookJoy,NULL);

//メインループ
gtk_main();

//後片付け
SDL_JoystickClose(joystick);
}
SDL_QuitSubSystem(SDL_INIT_VIDEO|SDL_INIT_JOYSTICK);
return 0;
}


どの段階で標準出力が取られるか知りたかったので最初にgtkinitをやったのだが
ジョイスティックのパラメータを出力できたのでメインループが怪しい。

今回はSDLのタイトルバーよろしくシステムトレーのtooltipsをデバッグウィンドウとして活用した。
ラストキーは大抵スタートボタンかシフトキーなのだが
ジョイスティックで絶対位置指定だとシフトキーが無いとはまると最悪で
SDLは途中でのUSBのダウンは考慮していないのでSDL部分の強制再起動ができたほうがよい。
そう考えると単にGUIとデーモンということでプロセス制御の方が良いかもと感じている。
その場合だと対象は問わないわけだ。

現段階だとマウスオンリーのゲームには使えるものの
キーボード専用・併用ゲームのジョイスティックの活用はできていないわけで
内心SDL未対応のプレステコントローラーを気にしつつも
実用アプリとしてはキーマクロの設定とかまでxtermベースでいいから作り込みたい
というかそういうのがあると便利だ。
posted by Xo_ox at 23:56| Comment(0) | FreeBSDアプリ | このブログの読者になる | 更新情報をチェックする
この記事へのコメント
コメントを書く
お名前:

メールアドレス:

ホームページアドレス:

コメント:

認証コード: [必須入力]


※画像の中の文字を半角で入力してください。
×

この広告は180日以上新しい記事の投稿がないブログに表示されております。