X



トップページLinux
1002コメント351KB
今夜も Wine で乾杯! - 21本目 [無断転載禁止]©2ch.net
■ このスレッドは過去ログ倉庫に格納されています
0001login:Penguin
垢版 |
2017/08/12(土) 21:18:15.22ID:tNr8ii2i
Linux上でWindowsのアプリを動作させるソフトウェア
Wineに関する情報交換スレ。

前スレ
今夜も Wine で乾杯! - 20本目
https://mao.2ch.net/test/read.cgi/linux/1455088008/

Wine本家
http://www.winehq.org/
http://wiki.winehq.org/

動作報告Wikiや過去ログなど
http://www.2chlinux.org/index.php?FrontPage

ここにパッチをうpするときはgzipやbzip2で圧縮した上で
base64などでエンコードしてください。おながいします。
動作報告は>>2のテンプレ使用を推奨。
0375login:Penguin
垢版 |
2018/02/10(土) 22:08:14.64ID:yxHJfpP2
ちがったかな
$ /opt/cxoffice/bin/wineserver --version
wine-2.8-8251-g8a457d1
0377login:Penguin
垢版 |
2018/02/15(木) 16:28:40.72ID:Adu/G50n
ubuntu studio 16.04LTS にwineを入れて、
JaneStyle を使える様にはなった。
書き込みテスト。
0378login:Penguin
垢版 |
2018/02/15(木) 22:20:39.99ID:feRq4tDS
>>368
まじかよ、Elonaさえ動けばいいのに。
0379login:Penguin
垢版 |
2018/02/15(木) 23:40:06.85ID:XEMKL93X
ARM機とかスマホしか持ってないわ
これから増えるんかね
0380login:Penguin
垢版 |
2018/02/16(金) 01:05:13.35ID:+Lqzmf2v
3DSとかもARMじゃないっけ
0381login:Penguin
垢版 |
2018/02/16(金) 08:01:15.26ID:TwuYLqrN
PS VitaもARMだしSwitchもARMだな
0382login:Penguin
垢版 |
2018/02/16(金) 08:03:04.95ID:8cWV/uet
最近のモバイル端末はだいたいARMだな
0383login:Penguin
垢版 |
2018/02/16(金) 08:03:51.98ID:8uACRqcY
ChromebookもARM機ある
0384login:Penguin
垢版 |
2018/02/16(金) 11:10:06.04ID:80vLUCQ5
WindowsもARM機が再登場する
0385login:Penguin
垢版 |
2018/02/16(金) 12:08:49.84ID:Q5rpQjEk
RTでコケて痛い目にあったからそれはないと思う。
ARMじゃない別のアーキテクチャで出す可能性はあるかも・・・
そしてまたコケるw
0386login:Penguin
垢版 |
2018/02/16(金) 12:24:21.66ID:Pj9ij8xw
>>385
いや出るから
HPとASUSがsnapdragon搭載Windows端末を発表済で春に発売予定
0387login:Penguin
垢版 |
2018/02/16(金) 13:41:56.11ID:QNuqSD5T
売れるとは限らないんだがなぁ
x86exeが何となくでも動くだけでいいのにな
0388login:Penguin
垢版 |
2018/02/16(金) 15:27:59.39ID:80vLUCQ5
x86が動くWin10のARMが出る
0389login:Penguin
垢版 |
2018/02/16(金) 16:20:26.93ID:1h3N4gI+
ちょっと調べてから否定しようよ
まあいいんだけどさ
0390login:Penguin
垢版 |
2018/02/16(金) 19:29:08.29ID:XuFK7/QF
古いけど、Ubuntu12.01 への Wine3.0のインストールに成功した。
git のソースから、./configure
途中、Font関連のファイルが見つからないエラーが出たので、
そのFontの「dev版」をapt-getでインストール。
その他、いくつかエラーが出るので、インストールしたりする。
その後、make, make install。
でインストールには成功するが、今まで動いていたWinアプリを起動してみると
日本語で文字化けしてる。wintricks font や regedit などをいじってみたが、
ダメだった。しかし、apt-get remove wineとしたあと、再度、
apt-get install wineとすると、なぜか、すぐに成功した。
wine --versionとすると、なぜか、3.1のままだった。
不思議な事に、文字化けはすっきり解決した状態で、Ver 3.1のWineが動いているらしい。
実際、Windowsとの互換性は明らかに向上した。
0391login:Penguin
垢版 |
2018/02/16(金) 19:30:26.21ID:XuFK7/QF
3.1じゃなく、3.0でした。スマソ
0392login:Penguin
垢版 |
2018/02/16(金) 19:37:33.92ID:XuFK7/QF
Wzエディタで、検索やGrepのダイアログのStatic Textの表示が途切れてしまう
不具合があったのが、Wine3.0では修正されている。これは使える。
0393login:Penguin
垢版 |
2018/02/16(金) 23:10:05.44ID:XuFK7/QF
透明色と MDI Window を組み合わせた自作WindowsアプリをWineで動くように修正中。
MDI Child Widnow のタイトルバーをドラッグすると遅くなるので原因を調査していた。
PreTranslateMessage() にくるメッセージを、自前でWindowに表示していたところ、
システム全体が実質的にハングアップした。マウスは動くが、タスクバーも反応しない。

ちなみに、透明色とMDI Windowの組み合わせが必ず遅くなるわけではない事は、
単純なテストプログラムを作って分かっている。ではなぜ、このプログラムに限っては
遅くなるのかが分からないので調査中だった。
0394login:Penguin
垢版 |
2018/02/17(土) 14:20:45.23ID:SLFwTllY
The Wine development release 3.2 is now available.

What's new in this release (see below for details):
- Separate implementation of USER controls for ComCtl32 v6.
- Multisample texture support in Direct3D.
- Support for HID gamepads.
- More event support in MSHTML.
- Obsolete DOS code removed.
- Various bug fixes.
0395login:Penguin
垢版 |
2018/02/17(土) 16:25:52.12ID:XDWn7PKv
廃れたDOSコードを削除、か
0396login:Penguin
垢版 |
2018/02/17(土) 16:26:50.92ID:KjUUJ1nJ
Wineは、Desktopの直接の子であるような、Win32における「OVERLAPED WINDOW」
的な物しか、XWindow の Window を作らないのだろうか??
Wineのソースをダウンロードして、FIXME(WARNでもいいはずだけど)で実行を調べてみた。
でも、良く分からない。ビルドとmake installに時間がかかるため、大変。
本当はもっと実験したいんだけど、時間的に難しくなる。

ソースを見ると、HWNDの親が Desktopの場合にだけ、XCreateWindowしているように
見える。これが、透明ウィンドウが遅くなる理由かもしれない。LinuxのNativeなWindowの
場合、実験した限り透明にしても速度が余り変わらない。でも、Wineがもし、自前で透明処理
をやっているとなれば遅くなるのも頷けるが。

1つのソースしか修正してないのに、ビルドが始まるまでに数十秒かかる。make installに
また数十秒。それに、wineserver -k などにも時間がかかるし。なんか、30年前のビルド環境
みたいだ。
0398login:Penguin
垢版 |
2018/02/17(土) 17:12:51.37ID:KjUUJ1nJ
>>397
今回に関しては、以下を読んで、git でやってます :
https://wiki.winehq.org/Git_Wine_Tutoria

home directory (~) にて、
$ git clone git://source.winehq.org/git/wine.git [ret]
とすると、wine ディレクトリが作成されるので、
$ cd wine
とします。そこで、
$ git config remote.origin.url [ret]
と入れて
git://source.winehq.org/git/wine.gi
と出れば、大体 DL 成功です。

~/wine/ にソースが DL されており、dlls に Win32 の DLL 群の実装があります。
例えば、user32 などのフォルダがあります。~/wine/server に wine の中枢の
プログラムが入っています。CreateWindow などは、dlls の方にありますが、
それを XWindowCreate に橋渡しするのは、server の方です。

オンラインで見たいなら、
https://source.winehq.org/git/wine.git/tree/HEAD:/dlls/user32
の win.c に CreateWindow 系のソースが入っており、

https://source.winehq.org/git/wine.git/blob/HEAD:/dlls/user32/win.c

で見られます。win.c の右の「raw」のボタンを押すと、ローカルの Wz エディタ
などでも見られます。
0399login:Penguin
垢版 |
2018/02/17(土) 17:16:25.31ID:KjUUJ1nJ
5ch からのアクセスは禁止されているらしいので、アドレスバーに上記の URL を
コピーしてアクセスしてください。

なお、make が遅い件は、make -j 4 などとして、マルチコア動作するとだいぶ良くなりました。
ただし、その場合、make にバグがあるので精密な神経の持ち主には、ストレスがたまります。
0400login:Penguin
垢版 |
2018/02/17(土) 17:19:14.92ID:KjUUJ1nJ
動きをトレースしたい場合、TRACE はデフォルトではメッセージが出ず、使い方を調べて
ないので、

FIXME( "Yamada Taro, CreateWindowEx, hWND=%08X", hWND );

みたいにやってます。
0401login:Penguin
垢版 |
2018/02/17(土) 17:23:11.12ID:KjUUJ1nJ
makeしたい人は、~/wine にて、

$ ./confiugre
$ make -j 4 2>build.log
$ make -j 4 install 2>inst.log
$ wineserver -k

で行けます。ここで、wz エディタを起動してみます。文字化けするようでしたら、

$ apt-get remove wine
$ apt-get install wine

で直ると思います。これは、一度やれば Ok のようです。
0402login:Penguin
垢版 |
2018/02/17(土) 17:40:09.90ID:KjUUJ1nJ
 call set_window_pos()
--> wine/dlls/user32/winpos.c
--> X11DRV_WindowPosChanging()
--> X11DRV_create_win_data()
--> dlls/winex11.drv/window.c :
  static void create_whole_window( struct x11drv_win_data *data )
--->
data->whole_window = XCreateWindow(
       data->display, root_window, pos.x, pos.y,
       cx, cy, 0, data->vis.depth, InputOutput,
       data->vis.visual, mask, &attr );

という流れのようです。
0403login:Penguin
垢版 |
2018/02/17(土) 17:41:22.28ID:KjUUJ1nJ
以下を見ると、親が、DesktopWindow の時のみ、create_whole_window() が呼び出されている
らしいことが分かります。

static struct x11drv_win_data *X11DRV_create_win_data( HWND hwnd, const RECT *window_rect,
                            const RECT *client_rect ) {
  Display *display;
  struct x11drv_win_data *data;
  HWND parent;
  if (!(parent = GetAncestor( hwnd, GA_PARENT ))) return NULL; /* desktop */
  /* don't create win data for HWND_MESSAGE windows */
  if (parent != GetDesktopWindow() && !GetAncestor( parent, GA_PARENT )) return NULL;
  if (GetWindowThreadProcessId( hwnd, NULL ) != GetCurrentThreadId()) return NULL;
  display = thread_init_display();
  init_clip_window(); /* make sure the clip window is initialized in this thread */
  if (!(data = alloc_win_data( display, hwnd ))) return NULL;
  data->whole_rect = data->window_rect = *window_rect;
  data->client_rect = *client_rect;
  if (parent == GetDesktopWindow()){
    create_whole_window( data );
    TRACE( "win %p/%lx window %s whole %s client %s\n",
        hwnd, data->whole_window, wine_dbgstr_rect( &data->window_rect ),
        wine_dbgstr_rect( &data->whole_rect ), wine_dbgstr_rect( &data->client_rect ));
  }
  return data;
}
0404login:Penguin
垢版 |
2018/02/17(土) 17:42:23.86ID:KjUUJ1nJ
>>402 の訂正:

CreateWindowExW()
--->
WIN_CreateWindowEx()
 call set_window_pos()
--> wine/dlls/user32/winpos.c
--> X11DRV_WindowPosChanging()
--> X11DRV_create_win_data()
--> dlls/winex11.drv/window.c :
  static void create_whole_window( struct x11drv_win_data *data )
--->
data->whole_window = XCreateWindow(
       data->display, root_window, pos.x, pos.y,
       cx, cy, 0, data->vis.depth, InputOutput,
       data->vis.visual, mask, &attr );
0405login:Penguin
垢版 |
2018/02/17(土) 18:08:08.76ID:KjUUJ1nJ
ちなみに、
1. ~/.wine
2. ~/wine

は別物です。1は、binary をインストールした際に出来るデフォルトのフォルダ
ですよね。2. は、git からソースを持ってきた時にできるディレクトリです。

まずは、バイナリをインストールして動作してから、ソースを持ってきてください。
0406login:Penguin
垢版 |
2018/02/17(土) 18:27:32.24ID:1JDlaACg
半透明化はWin32API側でどうやっているの? Linuxネイティブなアプリでは?

SetLayeredWindowAttributesであれば、user32.dllからUSER_Driverを介してwinex11.drvが呼ばれて、
window.c内のsync_window_opacityで_NET_WM_WINDOW_OPACITYにα値を設定している様子が見て取れる
ttps://github.com/wine-mirror/wine/blob/wine-3.2/dlls/winex11.drv/window.c#L435

TRACEで表示しているメッセージを確認するには環境変数WINEDEBUGを使って、
WINEDEBUG=win,x11 みたいにカンマ区切りで指定する。細かくは
ttps://wiki.winehq.org/Wine_Developer%27s_Guide/Debug_Logging に書いてある。

あとは、dlls配下の各ディレクトリでもmake && sudo make instlallできるので
特定のDLLファイルしか変更しないのであれば、この方法でビルド時間を短縮できるぞ。
0407login:Penguin
垢版 |
2018/02/17(土) 18:42:48.23ID:cvAP0C15
デバッグ大変だな。めんどくさそう。
仕事じゃないと俺はやらないだろうな。
0408login:Penguin
垢版 |
2018/02/17(土) 19:41:54.60ID:J9G7l4mf
自分はimm32関連(日本語入力)APIを修正しようとeclipceでコンパイル環境作ったはいいけど
ネイティブのウィンドウマネージャ関連の知識不足でソースの意味がわからず寝かせてある・・・
0409login:Penguin
垢版 |
2018/02/17(土) 19:54:02.08ID:Tf7u8zkg
>>406
Linux Nativeアプリの場合、32BIT COLOR にすると、A,R,G,B の 4つの値を
ドットの「色」として指定できます。Aがα値です。このようなことは、Windows
では出来ないと思います。Windowsの場合、CreateWindowExのdwExStyle に
WS_EX_LAYEREDを指定すると透明、半透明が扱えるようになります。

1.完全に「透明になる色」を24BIT値で1色指定できます。この色で描いたドットは、
 デスクトップまで透けて見えるようになります。見た目だけではなく、Windowメッ
 セージも下のWindowに伝達されてしまうことになりますが。

2.ドットごとではなく、Window全体のα値を1つ(1BYTE)だけ指定できます。
 ドットごとでは無いので、全体的に透明度が決まってしまいます。

1と2は完全に別の概念です。


LinuxのARGB値は、ドット毎に指定できるので、Windowsの機能を包含していると
言えます。逆に Windowsでは、同じ事は出来ないはずです。
0410login:Penguin
垢版 |
2018/02/17(土) 19:57:46.46ID:Tf7u8zkg
/wine/dlls/winex11.drv/x11drv.h
に次のような構造体があり、この whole_window というのが大事らしい:
/* x11drv private window data */
struct x11drv_win_data {
Display *display; /* display connection for the thread owning the window */
XVisualInfo vis; /* X visual used by this window */
Colormap colormap; /* colormap if non-default visual */
HWND hwnd; /* hwnd that this private data belongs to */
Window whole_window; /* X window for the complete window */
Window client_window; /* X window for the client area */
RECT window_rect; /* USER window rectangle relative to parent */
RECT whole_rect; /* X window rectangle for the whole window relative to parent */
RECT client_rect; /* client area relative to parent */
XIC xic; /* X input context */
BOOL managed : 1; /* is window managed? */
BOOL mapped : 1; /* is window mapped? (in either normal or iconic state) */
BOOL iconic : 1; /* is window in iconic state? */
BOOL embedded : 1; /* is window an XEMBED client? */
BOOL shaped : 1; /* is window using a custom region shape? */
BOOL layered : 1; /* is window layered and with valid attributes? */
BOOL use_alpha : 1; /* does window use an alpha channel? */
int wm_state; /* current value of the WM_STATE property */
DWORD net_wm_state; /* bit mask of active x11drv_net_wm_state values */
Window embedder; /* window id of embedder */
unsigned long configure_serial; /* serial number of last configure request */
struct window_surface *surface;
Pixmap icon_pixmap;
Pixmap icon_mask;
unsigned long *icon_bits;
unsigned int icon_size;
};
0411login:Penguin
垢版 |
2018/02/17(土) 20:00:42.49ID:Tf7u8zkg
>>406
後半の二つ。自分に取っては、かなり貴重な情報です。助かります。
0412login:Penguin
垢版 |
2018/02/17(土) 20:11:22.91ID:Tf7u8zkg
>>406
XWindow で透明化。これはやってみて実際に出来ました:
https://stackoverflow.com/questions/39906128/
how-to-create-semi-transparent-white-window-in-xlib

子ウィンドウを入れてみても、ちゃんと出来ました。

ただし、子ウィンドウには、Win32のような、タイトルバーは付ききませんでした。
XWindow に詳しい人によれば、XWindow の Window Manager は、
Win32 の MDI のような事は、サポートしていないらしいです。絶対か
どうかは分かりませんが。

実験してみると、子ウィンドウも、親ウィンドウも、ABI で、XMoveWindow
でスムーズに動かせました。

ただし、Wineの場合、子ウィンドウに直線や文字が書かれていて、かつ、背景が透明な場合、
スムーズには動かせません。
0413login:Penguin
垢版 |
2018/02/17(土) 21:31:43.98ID:cvAP0C15
Win32APIだけじゃなく、X Windowについても知っておかないといけないのか
面倒臭っ・・・

普段使ってるだけでずいぶん楽してたんだな。
0414login:Penguin
垢版 |
2018/02/17(土) 22:08:41.04ID:eqmjcJnH
面白そうだけど時間ねえな……
>>410を見る限りだとuse_alphaは持ってるけどAlpha値自体はもうX11の構造体にいれてるのかな?
まあ遅くなる理由とか検討つかんけど……
0415396
垢版 |
2018/02/18(日) 01:13:22.20ID:DznsC7ZZ
WINEにおいて、

1. MDIのCMDIChildWndのCViewのCLIENT領域全体(子ウィンドウの
  中全体と言ってもよい) に >>409の1.の色を塗って、完全透明
  にしている時は、CMDIChildWndのタイトルバー(子ウィンドウの
  タイトルバー)をドラッグしても高速に動かせる。

2. 1のCViewの中に、pDC->LineTo()で直線を一本描いた状態にしてから
  同じ事をしようとすると、とても遅くなる。

3. 2.は、直線の代わりに pDC->TextOut() で文字を描いても同様に遅く
  なる。

4. 推定では、Wineは、Win32のCreateWindow系で、Parent Window が
  Desktop 以外の場合、XWindow の Window を使わず、自前で
  Parent Window の中に画像を合成して子Windowを模倣している。

5. 4.の様にしている理由は、推定だと Window Manager の種類による
  挙動の違いに患わされずに安定して Emulation するためかも知れ
  ない。

6. その結果、XWindow の ARGB 値による高速な透明処理を利用できな
  くなり、低速になる。

7. しかし、なるべく低速にならないようにするため、少なくとも、
  子ウィンドウの中全体が完全透明な場合については、対策が取ら
  れており、高速にドラッグできるようになっている。

8. その結果、2, 3 のような条件の時のみ遅くなると推定される。
0416login:Penguin
垢版 |
2018/02/18(日) 01:24:45.49ID:smHwezpH
推定じゃなくて直接聞いたら?
0417login:Penguin
垢版 |
2018/02/18(日) 01:56:23.18ID:HH6qVqdM
>>416
どうやって?
0418406
垢版 |
2018/02/18(日) 02:29:31.78ID:dARugMLm
>>415
すまん、ウィンドウの半透明化処理(LWA_ALPHA)と勘違いしてた。
ウィンドウ領域内の特定色で書いた箇所を透過させる場合(LWA_COLORKEY)ね。

ソースを調べたら、WineではX11のShape Extensionを使ってウィンドウの形状を変更することで、
「見た目だけではなく、Windowメッセージも下のWindowに伝達すること」を実現しているようだ。

具体的にはupdate_surface_region()で1ピクセルごとにピクセル値を比較して、
XShapeCombineRectanglesに指定する矩形領域を作っている。
ttps://github.com/wine-mirror/wine/blob/2936f3f9bb9dc01c595498a821d6adb6775b62cc/dlls/winex11.drv/bitblt.c#L1616

415の2.,3.の条件だと遅くなるとすると、ピクセル値を比較する処理は1.と同じなので、
矩形が大量になったときに、X側で描画性能が低下するのではないかと思うぞ。

StackOverflowの記事はShape Extensionを使っていないので筋違いだ。
0419login:Penguin
垢版 |
2018/02/18(日) 04:20:47.62ID:HH6qVqdM
>>418
なるほど。

1つ質問です。

はっきりとは書いてなかったのですが、>>415の遅くなる条件であるところの 2,3 の場合
においても、CMainFrame、つまり、アプリケーション全体の Main の Window のタイトルバーを
ドラッグした場合は、遅くなりません。いたって高速にドラッグできます。

>>418 が正しいなら、どうして、X は、この場合だけは速く、CMDIChildWnd の場合だけは
遅く動作するのでしょうか???
0420login:Penguin
垢版 |
2018/02/18(日) 04:57:21.33ID:HH6qVqdM
↓の構造体の dc_rect の矩形が子ウィンドウを模倣するために使われているかも知れません:
/* X physical device */
typedef struct {
  struct gdi_physdev dev;
  GC      gc;     /* X Window GC */
  Drawable   drawable;
  RECT     dc_rect;    /* DC rectangle relative to drawable */
  RECT     *bounds;    /* Graphics bounds */
  HRGN     region;    /* Device region (visible region & clip region) */
  X_PHYSPEN   pen;
  X_PHYSBRUSH  brush;
  int      depth;    /* bit depth of the DC */
  ColorShifts *color_shifts; /* color shifts of the DC */
  int      exposures;  /* count of graphics exposures operations */
} X11DRV_PDEVICE;

BOOL X11DRV_LineTo( PHYSDEV dev, INT x, INT y ) {
  X11DRV_PDEVICE *physDev = get_x11drv_dev( dev );
  POINT pt[2];
  GetCurrentPositionEx( dev->hdc, &pt[0] );
  pt[1].x = x;
  pt[1].y = y;
  LPtoDP( dev->hdc, pt, 2 );
  add_pen_device_bounds( physDev, pt, 2 );
  if (X11DRV_SetupGCForPen( physDev ))
    XDrawLine(gdi_display, physDev->drawable, physDev->gc,
         physDev->dc_rect.left + pt[0].x, physDev->dc_rect.top + pt[0].y,
         physDev->dc_rect.left + pt[1].x, physDev->dc_rect.top + pt[1].y );
  return TRUE;
}
0421login:Penguin
垢版 |
2018/02/18(日) 05:32:50.78ID:HH6qVqdM
/wine/dlls/user32/painting.c の中の、
// Set the visible region and X11 drawable for the DC associated to a given window.
static void update_visible_region( struct dce *dce )
の中に、
USER_Driver->pGetDC( dce->hdc, dce->hwnd, top_win, &win_rect, &top_rect, flags );
とあって、
void CDECL X11DRV_GetDC( HDC hdc, HWND hwnd, HWND top, const RECT *win_rect,
const RECT *top_rect, DWORD flags )
が呼び出される。引数に hwnd と top、win_rect と top_rect が対になっているらしいことに注意。

この関数の中で、x11drv_escape_set_drawable escape; の
escape.dc_rect に、win_rect の top_rect の (left, top) からの相対座標が入れられる。

そして、ExtEscape( hdc, X11DRV_ESCAPE, sizeof(escape), (LPSTR)&escape, 0, NULL );
と ExtEscape() が呼び出される。

escape = X11DRV_ESCAPE;
escape.code = X11DRV_SET_DRAWABLE;
in_data = &escape;
の状態で、

X11DRV_PDEVICE *physDev = get_x11drv_dev( dev );
const struct x11drv_escape_set_drawable *data = in_data;
physDev->dc_rect = data->dc_rect;
となる。physDev->dc_rect が、>>420 の dc_rect に他ならない。

つまり、HWND hwndのwin_rectの、HWND topの左上座標からの相対座標が、
physDev->dc_rect に入ることになると思われる。hwndの「最上位の親」が
topだとすると、topだけが XCreateWindow()された本物のWindowであって、
hwnd は 擬似Windowであるとして辻褄が合う。hwndへの描画は、
実は座標だけを修正して top の(本物の X )Window に書き込まれているだけ
かも知れない。
0422login:Penguin
垢版 |
2018/02/18(日) 12:58:43.05ID:HH6qVqdM
結論的には、Wine では、完全透明色が設定されている全ての LAYERED_WINDOW に
対して、Idle状態の時か、または、50(ms) 毎に、Windowアプリのメッセージループ
の中から自動的に、>>418 の update_surface_region() が呼び出されるようになっ
ているらしいです。この条件のWindowがあって、かつ、update_surface_region() の
処理が重い場合に、動作が遅くなる可能性が高いです。Dirty Bitのようなものは、
今のところ見つかっていませんので、何もしなくても常に重くなるのでしょうか。

【詳細】
flush_window_surfaces() なる関数が、定期的に呼び出される。
典型的なタイミングは、PeekMessage() の中からであり、GetMessage()では、
check_for_driver_events() を介して呼び出される。
flush_window_surfaces() の中に次のようなマクロ呼び出しがある :
LIST_FOR_EACH_ENTRY( surface, &window_surfaces, struct window_surface, entry )
surface->funcs->flush( surface );
ここで、
/* iterate through the list using a list entry */
#define LIST_FOR_EACH_ENTRY(elem, list, type, field) \
for ((elem) = LIST_ENTRY((list)->next, type, field); \
&(elem)->field != (list); \
(elem) = LIST_ENTRY((elem)->field.next, type, field))
であり、上記は、window_surfaces リストに登録されている全ての
window_surface *surface について、window_surface_funcs *funcs の
関数ポインタ flush の関数を呼び出すことになり、結局、
関数 x11drv_surface_flush() が呼び出されることになる。

関数 x11drv_surface_flush() の中に
if (surface->is_argb || surface->color_key != CLR_INVALID) update_surface_region( surface );
とある。update_surface_region() は、>>418 に書かれている関数。
0423396=422
垢版 |
2018/02/18(日) 13:53:01.38ID:HH6qVqdM
該当の条件の時、新たには何の描画もしていない静止状態でも、Linuxのシステム・モニターで
CPUパワーが膨大に消費されていることを確認しました。このことは、>>422 を裏付ける物です。
だとすると、Dirty Bit を導入して、update_surface_region()の頻度を下げれば、この低速化
は修正できる可能性が出てきました。LineTo, TextOut, MoveWindow, SetWindowPos,
ShowWindow などを使ったときだけ、DirtyBit を 1にして、1の時だけ update_surface_region()
を呼び出し、呼び出した後には 0 にすれば良いのではないかということです。

条件は:

1. WINEを使用して Windowsアプリを走らせていること。
2. アプリ内で CreateWindowEx() のdwExStyle に WS_EX_LAYERED を指定して Windowを作成
  済みであること。
3. さらに、SetLayeredWindowAttribute() に LWA_COLORKEY を指定して「完全透明色」を
  指定していること。
4. その Window の背景を「完全透明色」で消去していること。
5. その Window 内部に通常色で、LineTo() や TextOut() によって図形や文字を
  描いた後であること。

です。

MDIを使っている事や、CMDIChildWnd 動かすかどうかは関係ない様です。

CMainFrame をドラッグした時には遅く感じないのは、その動作についてはシステムが CPU
の優先順位を上げているからではないかと思います。
0424396
垢版 |
2018/02/18(日) 14:19:00.83ID:HH6qVqdM
>>418 さんの指摘は凄く役立ちました。有難うございます。

ただし、こっちの調査不足のせいが大きいのでしょうが、以下の部分は、今回の
結果とは違っていたようです:
>415の2.,3.の条件だと遅くなるとすると、ピクセル値を比較する処理は1.と同じなので、
>矩形が大量になったときに、X側で描画性能が低下するのではないかと思うぞ。

「X側の描画性能の低下」が原因ではなく、「ピクセル値を比較する処理」自体が、
実は「静止状態」でも、常時、大幅に増加していた、ということです。

何の変化がないときにでも、百万ピクセルを比較し、ランレングスを導き出す処理を、
原則的には秒間20回もやっています。

また、図形が複雑だと、システムコールを呼び出す回数がランレングスの変化の回数倍
されます。例えば、直線をN本引いた場合、システムコールの回数が原則 3N (倍)になります。
縦方向が500ドットのWindowで、1本の直線を引いた場合、一回の処理で、最低でも
6,000回のシステムコールが呼び出されます。これが秒間20回も行われますので、
最低で、秒間12万回のシステムコールとなります。1回のシステムコールは、5(μs)
くらいはどうしてもかかるので、これだけで、0.6(秒)もかかってしまいます。
つまり、1秒間に0.6秒も無駄なシステムコールに時間を取られていると見積もれます。
直線を2本にすると、これだけでCPUがフルパワー状態になります。
0425396
垢版 |
2018/02/18(日) 14:38:25.11ID:HH6qVqdM
【厳密化】

1. 多分、6,000回ではなく、4,500回程度でした。
2. 比較処理自体は、図形の複雑さとは無関係にほぼ一定の重さです。
3. 図形が複雑な場合、システムコールの回数が増えます。
4. 横方向の1つのランレングスで、3つのシステムコールが呼ばれます。
5. 図形が何も描かれていない場合、1つのy座標に対して、1つのランレングスです。
6. N 本の直線の場合、大体で言えば、3N 個のランレングスになります。
7. だから、1つのy座標に対し、9N 個のシステムコールが呼ばれることになります。
8. 縦 500 ドットの場合、500*9N = 4500 N 回のシステムコールとなります。
9. よって、中に描かれている直線の本数が増えると、大体 O(N) で処理時間が
  増えます。
10. 文字を描いた場合、大体、文字の複雑さに比例して処理時間が増えます。
0426396
垢版 |
2018/02/18(日) 15:25:59.15ID:HH6qVqdM
>>425
【訂正】
「6.」のランレングスの個数は、3N個ではなく、2N+1 個でした。

お騒がせしました。
0427login:Penguin
垢版 |
2018/02/18(日) 16:08:39.77ID:dARugMLm
>>425
update_surface_regionの方が効率悪そうなので、X側の描画性能が原因と推測した部分は撤回する。

Shape Extensionの仕様を見ると、
矩形領域ではなくマスク用のPixmapを指定する方法も採れるから、
BitBltで効率よくマスクを作ればそっちの方が早くなるかもと思ってみたり。

あと、MDIウィンドウがXのWindowではないことは、xwininfoコマンドの結果でも確認できる。
0428396
垢版 |
2018/02/18(日) 17:01:36.91ID:hqeoLLBF
>>427
>矩形領域ではなくマスク用のPixmapを指定する方法も採れるから、
>BitBltで効率よくマスクを作ればそっちの方が早くなるかもと思ってみたり。

少なくともその方法だと、図形の複雑さによらずに安定して同じ処理時間で
済みますね。それと当然、BitBlt はハードウェアの補助が得られます。
あと、システムコールも全体で数回しか呼ばなて済みますし。
0429396
垢版 |
2018/02/19(月) 12:40:07.08ID:v4nZjJQ1
特定のアプリの場合にだけは、自作の user32.dll.so を読み込ませる事が出来ない。
WINEDLLPATH, WINEPATH を試してみたが、今のところ上手く行かない。
0430396
垢版 |
2018/02/19(月) 15:28:56.74ID:v4nZjJQ1
調べてみると、/wine/libs/wine/loader.c の中の build_dll_path()
関数の中で、環境変数 WINEDLLPATH が、読み取られ、: を 0x00に
変えてから、dll_paths[] 配列に : で区切られたそれぞれの文字列
の先頭アドレスを代入している。

しかし、その処理に入る前に、get_dlldir() 関数が NULL 以外を返した場合、
dll_paths[0] にその関数が返した文字列のアドレスを入れてしまう。

そして、実際の get_dlldir() は、"/usr/local/bin/../lib/wine"
という文字列を返してくるので、そっちのパスの方が、WINEDLLPATH
の優先順位よりも上になってしまう。そして、

/* retrieve the default dll dir */
const char *get_dlldir( const char **default_dlldir )
{
*default_dlldir = DLLDIR;
return dlldir;
}
となっているが、DLLDIR が全ての拡張子の全ファイルを全文検索しても
どこにも定義されておらず、参照されているのも唯一この関数だけだった。

これだと、WINEDLLPATH で「Override」できないのも当然だけど、
そもそも、DLLDIR がどこで定義されているのかが分からないので
誰かの教えをいただきたいです。
0431login:Penguin
垢版 |
2018/02/19(月) 15:47:21.84ID:qkc2SRTD
見つかりました。wine/libs/wine/Makefileの中で、
config_EXTRADEFS = \
  -DBINDIR='"${bindir}"' \
  -DDLLDIR='"${dlldir}"' \
  -DLIB_TO_BINDIR=\"`$(MAKEDEP) -R ${libdir} ${bindir}`\" \
  -DLIB_TO_DLLDIR=\"`$(MAKEDEP) -R ${libdir} ${dlldir}`\" \
  -DBIN_TO_DLLDIR=\"`$(MAKEDEP) -R ${bindir} ${dlldir}`\" \
  -DBIN_TO_DATADIR=\"`$(MAKEDEP) -R ${bindir} ${datadir}/wine`\"
というのが。単語検索していたのが鬼門でした。
0432396
垢版 |
2018/02/19(月) 19:22:56.55ID:WuPPo4Ry
loader.c の build_dll_path() の修正と、user32.dll.so の修正を、
自分のアプリだけに適用する事に成功しました。
0433396
垢版 |
2018/02/20(火) 03:10:39.77ID:nCkYsCc8
surface が NULL ではない場合、xxx->pLineTo() は、X11DRV_LineTo()
ではなく、dibdrv_LineTo() が呼び出されるような・・・。
0434396
垢版 |
2018/02/20(火) 05:59:07.45ID:nCkYsCc8
DirtyBit を使って、例の update_surface_region() は、
描画してないときには全く呼ばれないようにできたんですが、
まだかなり遅いです。何もしてないときにも CPU パワーがかなり消費されています。
その間、update_surface_region() が呼び出されていないことは、FIXME で
表示して確認が取れています。

なお、>>433は、windrv_LineTo() の方の間違いでした。windrv_LineTo() の場合、
簡単に surface にアクセスできるので、surface に bNeedToUpdate のような
フラグを追加して、それを 1 にして dirty bit にしています。

なぜ遅いままなのかは謎です。
0435login:Penguin
垢版 |
2018/02/20(火) 06:05:44.26ID:AmaI1OSV
2ch(5ch?)は人は多いけど基本的にユーザーレベルの人ばかりだから技術的な書き込みしても反応は鈍いね
Qiitaにでも書いてみたらどう?
0436login:Penguin
垢版 |
2018/02/20(火) 18:28:22.71ID:QMmYbOOf
本家にパッチ投稿もお願いしますw
0437396
垢版 |
2018/02/20(火) 18:49:16.00ID:nCkYsCc8
遅い原因の1つは、Onldle の中にあることが分かりました。

1. 必要があって、外部ツールによるファイルの更新チェックをしていたところ、
  それがとんでもなく重い事が判明。

2. SetTimer で CMainFrame にタイマーをしかけていると、CMainFrame::OnCmdMsg()
  が、WM_TIMER メッセージが来る度に、それを何倍にも掛け算した回数だけ呼び
  出される事が判明。例えば、秒間 N 回のタイマーを仕掛けると、OnCmdMsg が、
  10*N 回 呼び出されるような感じです。タイマーメッセージがくる度に、
  メニューなどを更新するためのメッセージが来ているような気がします。
  多分、本家 Windows では、タイマーメッセージが来ても、そのような事には
  ならないんじゃないかと思います。 通常、OnCmdMsg は頻度が低いことが
  前提になっているので、その中で多少重い処理をしていると、とんでもなく
  重たくなる。

なお、透明色を使っている場合、update_surface_region の中の処理の軽重に
よって、描画速度が大幅に変わることも確認しました。

ランレングスを数えてリージョンを add_row している箇所を、単純に何も
数えずに行全体で、1個だけ add_row するように修正してみると、描画が
だいぶ速くなりました(透明にはなりませんが。)。
0438login:Penguin
垢版 |
2018/02/20(火) 18:56:15.10ID:nCkYsCc8
>>436
そうしたいんですが、何かと大変なんです。

1. メールアドレスの登録するのが、Linuxからだと大変。
2. git の仕様が良く分からない。
3. 回線が遅いので、Project を Fork して、git が Tree 全体を
 アップロードしようとしてしまったりなんかすると大変な事になる。
4. だから、一部のファイルだけを UL したいが、コミュニケーションの
 問題から事情を伝えて、そこまで行くかどうか分からない。
5. だから、独自にオーバーライドしようとしたのが、先日からここでも
 書いていたことです。loader を修正して、かつ、環境変数を変えて
 やれば、本家と統合しなくても、部分書き変えのような事が出来る。
0439login:Penguin
垢版 |
2018/02/20(火) 18:59:20.15ID:TT83z4l8
>>438
githubからでもプルリクエスト受け付けてるよ
0441login:Penguin
垢版 |
2018/02/20(火) 19:12:23.54ID:hMkWg0ue
描画が早くなるならありがたい
素のWindowsより異様に遅いゲームとかあるし
0442396
垢版 |
2018/02/20(火) 20:06:46.63ID:ErFPSqeX
結論だけ書いておくと、surfaceが非NULLの時の LineTo の実体は、
windrv_LineToで、surface を lock する以外は、dibdrv_LineTo と同じ。
dibdrv_LineToは、bits なるpixel配列に 32BITの色値を書き込む。
実際は汎用性のため、必ずandしてからxorする。SOLIDモードの場合、and値は0。
xor値は、00RRGGBB で、最上位バイトは0。
実際への画面の反映は、定期的に「xxx->flush」なる関数ポインタを介して、
x11drv_surface_flush() を呼び出す事で行う。その中で、議題になっている
update_surface_region() も呼ばれる。最後に bits の内容を実画面に反映させる
ため、XShmPutImage()または、XPutImage(), XFlush()を行う。

surface->image の pixel バッファは、bits[] 配列と共通らしい。

つまり、Linux の描画関数は全く使わずに自前で画像をpixel配列に合成してから、
定期的に、XPutImage() などで実が面に描き込む、というようなことをやっているらしい。
0443396
垢版 |
2018/02/21(水) 16:48:35.77ID:SJPTXnf1
update_surface_region() が遅いんだと思って、
XShape の作成を、XShapeCombineRectangles() を使う方式から、
XCreateBitmapFromData() と XShapeCombineMask()
を使う方式へと変えた。

すると、1146 x 745 のサイズの Window で、

1. 自前のコードによる 1bit の mask bitmap 作成にかかる時間 :
796 (us)

2. XCreateBitmapFromData() と XShapeCombineMask() にかかる時間 :
38 (us)

3. XShmPutImage() と XFlush() ; x11drv_surface_flush()内 の両方にかかる時間 :
94 (us)

4. MDI Child Wnd をドラッグするときに、x11drv_surface_flush() が
  呼び出される時間間隔 :
  3〜100 (ms) ---> 秒間 10回〜330回

5. 肉眼で描画が更新されるまでに x11drv_surface_flush() が呼び出される回数 :
数10回 以上あることが多い。


1.〜4. だけみると、高速に処理されている様に見えるけど、5. だけが理解できない。
カウンタを設けて数えてみると、実際に目では更新が起きてないのに、
ちゃんと、1〜3 の全ての関数が数十回も呼び出されている。

ちゃんと「XFlush()」されているのに、なぜだろう????
0444login:Penguin
垢版 |
2018/02/21(水) 17:03:51.93ID:SJPTXnf1
XSync() を追加しても、XShmPutImage() を XPutImage() に修正しても、
何をやっても見た目だけが更新されない。
0445login:Penguin
垢版 |
2018/02/21(水) 17:09:56.25ID:iuEniYB2
>>443

> update_surface_region() が遅いんだと思って、
> XShape の作成を、XShapeCombineRectangles() を使う方式から、
> XCreateBitmapFromData() と XShapeCombineMask()
> を使う方式へと変えた。

こうするとなんで速くなるの?
0446396
垢版 |
2018/02/21(水) 17:35:51.39ID:SJPTXnf1
>>445
それは、
>>427-428
の部分と深く関係しています。
0447login:Penguin
垢版 |
2018/02/21(水) 17:39:34.11ID:SJPTXnf1
X の同期の問題ではなく、MDI Child Wnd の TITLE BAR をドラッグするとき、
なぜか、数秒経ってから、x11drv_surface_flush() がまとめて何十回も呼び出されている
可能性があるかも・・・。

デバッグ表示をリアルタイムで見ていると、見た目が変化する直前までデバッグ表示が
全く出ず、見た目が変化する直前に、どどっと、数十回分の x11drv_surface_flush()
がまとめて出てくる、という現象が起きている。
0449396
垢版 |
2018/02/21(水) 17:57:13.78ID:SJPTXnf1
以下のコードが気になる。

message_count を減少させるのは唯一、wait_message() しか見つからなかった。
後は、check_for_driver_events() の中で単調増加してしまう。
メッセージがたまって初めて、flush_window_surfaces() が呼び出されるような
コードにも

/* check for driver events if we detect that the app is not properly consuming messages */
static inline void check_for_driver_events( UINT msg )
{
  if (get_user_thread_info()->message_count > 200) {
    flush_window_surfaces( FALSE );
    USER_Driver->pMsgWaitForMultipleObjectsEx( 0, NULL, 0, QS_ALLINPUT, 0 );
  }
  else if (msg == WM_TIMER || msg == WM_SYSTIMER) {
    /* driver events should have priority over timers, so make sure we'll check for them soon */
    get_user_thread_info()->message_count += 100;
  }
  else get_user_thread_info()->message_count++;
}

static DWORD wait_message( DWORD count, const HANDLE *handles, DWORD timeout, DWORD mask, DWORD flags )
{
  DWORD ret = USER_Driver->pMsgWaitForMultipleObjectsEx( count, handles, timeout, mask, flags );
  if (ret == WAIT_TIMEOUT && !count && !timeout) NtYieldExecution();
  if ((mask & QS_INPUT) == QS_INPUT) get_user_thread_info()->message_count = 0;
  return ret;
}
0450396
垢版 |
2018/02/21(水) 17:58:18.49ID:SJPTXnf1
BOOL WINAPI DECLSPEC_HOTPATCH PeekMessageW( MSG *msg_out, HWND hwnd, UINT first, UINT last, UINT flags )
{
  MSG msg;

  USER_CheckNotLock();
  check_for_driver_events( 0 );

  if (!peek_message( &msg, hwnd, first, last, flags, 0 ))
  {
    DWORD ret;

    flush_window_surfaces( TRUE );
    ret = wow_handlers.wait_message( 0, NULL, 0, QS_ALLINPUT, 0 );
    /* if we received driver events, check again for a pending message */
    if (ret == WAIT_TIMEOUT || !peek_message( &msg, hwnd, first, last, flags, 0 )) return FALSE;
  }

  check_for_driver_events( msg.message );

  /* copy back our internal safe copy of message data to msg_out.
   * msg_out is a variable from the *program*, so it can't be used
   * internally as it can get "corrupted" by our use of SendMessage()
   * (back to the program) inside the message handling itself. */
  if (!msg_out)
  {
    SetLastError( ERROR_NOACCESS );
    return FALSE;
  }
  *msg_out = msg;
  return TRUE;
}
0451396
垢版 |
2018/02/21(水) 18:24:27.09ID:SJPTXnf1
>>448
PeekMessage()を回しているだけの時で、メッセージがキューに残り続けている場合、
メッセージを 200回 (100回?) PeekMessage するまでは、flush_window_surfaces()
されないコードになっている気がしませんか・・・。
0452login:Penguin
垢版 |
2018/02/21(水) 18:32:33.53ID:SJPTXnf1
GetMessage() の方もほぼ同じような感じかも。。

とにかく、flush_window_surfaces() がとてつもなく長い間、呼ばれないことが
あるコードになっている様に見えて、実際、実験してもそう思える。
0453login:Penguin
垢版 |
2018/02/21(水) 18:38:45.85ID:SJPTXnf1
そうか、もともとは、dirty bit 方式じゃないから、flush_window_surfaces() を呼ぶと、
再計算する必要が無いのに必ず再計算されてしまうから、呼び出す頻度を下げるしか
高速化する方法が無いために、そんな風なコードになっているのかもしれない。
0454login:Penguin
垢版 |
2018/02/21(水) 18:41:37.14ID:SJPTXnf1
そして、flush_window_surfaces() のコードでは、最低でも 50 (ms) の時間が経っていない場合には
処理をスキップするコードになっている。とことん、flush() しないコードになってしまっている。
これらが原因か・・・。

void flush_window_surfaces( BOOL idle )
{
  static DWORD last_idle;
  DWORD now;
  struct window_surface *surface;

  EnterCriticalSection( &surfaces_section );
  now = GetTickCount();
  if (idle) last_idle = now;
  /* if not idle, we only flush if there's evidence that the app never goes idle */
  else if ((int)(now - last_idle) < 50) goto done;

  LIST_FOR_EACH_ENTRY( surface, &window_surfaces, struct window_surface, entry )
    surface->funcs->flush( surface );
done:
  LeaveCriticalSection( &surfaces_section );
}
0455396
垢版 |
2018/02/21(水) 18:58:29.00ID:SJPTXnf1
元々の Windows では、LineTo などを実行すると瞬間的に実画面に反映される。
だから、そもそも Flush せずに見えないままの描画が残っているなんて時間は
ほぼ 0。

そう考えると、。Wine のこの実装はおかしいな・・・。

1. そもそもメッセージループを回している時しか flush される可能性が全くない
  実装になってしまっているらしい。

2. メッセージループを回していても、メッセージがキューに残っている時は、
  100回程度メッセージを読まない限り、flush されない。


Windows を「エミュレート」するのであれば、例えば、描いてから 50(ms) 経てば、
必ず flush する、という実装でなくてはならないはず。

Linux では、タイマー処理が難しいのかな???
0456login:Penguin
垢版 |
2018/02/21(水) 19:03:48.84ID:SJPTXnf1
1. Windows (もちろん、NT系) の場合は、メッセージ・ループ長く帰ってこないプログラムも
 絶対ダメというわけではない。

2. そういうプログラムの場合、Wine 側は、描画を、一定時間間隔で、別スレッドの
  タイマー割り込みの中などで flush するようにしないと、 正しく、Emulation 出来
  ないはず。
0457login:Penguin
垢版 |
2018/02/21(水) 19:05:31.10ID:SJPTXnf1
割り込みなら、別スレッドを起こす必要が無かった。スマソ。

あと、誤字訂正:
誤: メッセージ・ループ長く帰ってこないプログラムも
正: メッセージ・ループに長く帰ってこないプログラムも
0458login:Penguin
垢版 |
2018/02/21(水) 19:13:35.32ID:iuEniYB2
うーん
これがボトルネックになっているようには見えない

申し訳ないんだけど描画が遅くなる条件をもう一度書いてもらっていいですか?
0459396
垢版 |
2018/02/21(水) 23:23:31.14ID:4g3iPm6d
うーんと。それはまあちょっと置いておいて、、、。

MDI Child Wnd の TITLE BAR を Drag している最中、メッセージループは一つも
回ってないんじゃないかと思う。

だから、>>455 の「1.」に書いた「flushされる必要条件」が満たされてない。

そのため、surface の pixel配列の内容が実画面に反映されないのではなかろうか。
0460396
垢版 |
2018/02/21(水) 23:38:56.14ID:4g3iPm6d
ドラッグ中にマウスを静止させてしばらく待っていると、 x11drv_surface_flush() と
flush_surface_region() の両方が呼び出された事が FIXME 出力で確認できた。
静止させずにマウスをドラッグし続けると、FIXME 出力が全くでない。
しかも、それを行った時間が長いと、マウスボタンを離した後も、非常に長い間
システム全体がハングアップした様な状態になる。そして、長い沈黙の後、FIXME出力が
出る。

この沈黙の時間は、それまでドラッグしていた時間に比例しているように思える。

なぜだろう??? これはどこかに「何かが溜まっている」時の現象の様に思える。
しかし、>>449 を見ると、message_cnt は、減らされるときは、一期に 0 になるのであって、
少しずつ減らされるわけではない。

謎だ・・・。
0461login:Penguin
垢版 |
2018/02/21(水) 23:42:58.43ID:iuEniYB2
単にイベントを送信する側でバッファリングしてるんじゃないの?
0462login:Penguin
垢版 |
2018/02/21(水) 23:50:18.32ID:iuEniYB2
まあ最初からマルチスレッド意識して書いた感じではないなと思うけど90年代から開発されてるわけだしそのへんはご愛嬌じゃない
0463396
垢版 |
2018/02/22(木) 00:35:51.88ID:9+xI5ulA
>>449-450 辺りのソースを見ていて、背理法で語ってみたい。

仮定1:「Drag 中でも、PeekMessage されている。」 と仮定する。
仮定2. 透明化した際に重くなるのは、flush_window_surfaces() と、
    そこから呼び出される update_surface_region() のみだと仮定する。
補足: update_surface_region() は、透明化のための Shape の作成の処理が
    されるので、重い。
    flush_window_surfaces() は、update_surface_region() を呼び出す
    以外にも、XPutImage() 系の処理が入るので、さらに重い。

仮定2については、透明化時に、それ以外に重い関数が出現する可能性を否定は
できない。なぜなら、surface の処理は、透明化しない時には行われないかも
知れないからである。その場合、LineTo の関数が切り替わる。

1. FIXME で見ている限り、ドラッグ中は、それらの関数は全く呼び出されない。

2. >>450 を見ると、PeekMessage した際に、キューにメッセージが残ってないなら、
  flush_window_surfaces() が呼び出されるはず。

3. 「仮定2」 の関数は呼ばれてないのだから、キューのメッセージは、透明化され
 てない場合と同程度に高速に処理されてもおかしくない。

4. もしそうであれば、キューのメッセージはあっという間に「空」になるはず。

5. 空になったとしたら、「2.」の通りに、flush_window_surfaces() が呼び出されるはず。

6. しかし、「1.」で述べたように、実験的には、flush_window_surfaces() は呼び出されてない。

7. 矛盾である。

8. 背理法により、これは、仮定の少なくとも一方が間違っている事を意味する。
0464396
垢版 |
2018/02/22(木) 00:45:26.84ID:9+xI5ulA
あるいは、Peek されるだけで、一部のメッセージは取り残されるなら・・・。
0465396
垢版 |
2018/02/22(木) 01:30:28.67ID:9+xI5ulA
Wine で、MDI Child Wnd の TITLE BAR をドラッグしたときの処理について。

DEFWND_DefWinProc( HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam )
-->case WM_NCLBUTTONDOWN
-->NC_HandleNCLButtonDown()
-->case HTCAPTION
-->SendMessageW( hwnd, WM_SYSCOMMAND, SC_MOVE + HTCAPTION, lParam )
-->DEFWND_DefWinProc( HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam )
-->case WM_SYSCOMMAND
-->NC_HandleSysCommand()
-->WINPOS_SysCommandSizeMove()

最後の関数は、タイトルバーをドラッグを開始直後に一回だけ呼び出される事
を確認した。その後のドラッグ中は呼び出されず、この関数の内部でドラッグ
の処理がされている可能性が高い。

この最後の関数の中に、次のような、メッセージ・ループがある:
if (!GetMessageW( &msg, 0, 0, 0 )) break;
if (CallMsgFilterW( &msg, MSGF_SIZE )) continue;

/* Exit on button-up, Return, or Esc */
if ((msg.message == WM_LBUTTONUP) ||
((msg.message == WM_KEYDOWN) &&
((msg.wParam == VK_RETURN) || (msg.wParam == VK_ESCAPE)))) break;

if ((msg.message != WM_KEYDOWN) && (msg.message != WM_MOUSEMOVE)) {
TranslateMessage( &msg );
DispatchMessageW( &msg );
continue; /* We are not interested in other messages */
}
0466396
垢版 |
2018/02/22(木) 01:32:06.83ID:9+xI5ulA
さらに次のような、枠だけを書くか、実際に動かしてしまうコードが続く :

if (!DragFullWindows)
draw_moving_frame( parent, hdc, &sizingRect, thickframe );
else {
RECT rect = sizingRect;
MapWindowPoints( 0, parent, (POINT *)&rect, 2 );
SetWindowPos( hwnd, 0, rect.left, rect.top,
rect.right - rect.left, rect.bottom - rect.top,
( hittest == HTCAPTION ) ? SWP_NOSIZE : 0 );
}
0467396
垢版 |
2018/02/22(木) 02:01:49.71ID:9+xI5ulA
あー。

FIXMEを消してしまっていただけだった・・・。

update_surface_region() や、flush_window_surfaces() は、
ドラッグ中でも、マウスを静止してしばらくすると呼ばれるようです。
マウスを動かしつづけていると呼ばれないらしい。
0468396
垢版 |
2018/02/22(木) 02:44:50.36ID:9+xI5ulA
【やっと原因判明】

CMainFrame を WS_EX_LAYERED で透明化していて、今回の条件に当てはまる時に、
CMDIClientWnd のタイトルバー上でマウスの左ボタンを押し始め、そのまま
マウスをずっと動かし続けると、WINPOS_SysCommandSizeMove() の中の

if (!GetMessageW( &msg, 0, 0, 0 )) break;

の部分の GetMessageW() 関数の中で停止してしまう。詳細は、GetMessageW() の
中で、メッセージキューが空だった場合に呼び出されるところの、

wait_objects()    ;dlls/user32/message.c
-->wow_handlers.wait_message()
-->wait_message()  ;dll/user32/winproc.c
-->USER_Driver->pMsgWaitForMultipleObjectsEx()
-->X11DRV_MsgWaitForMultipleObjectsEx()

の最後の関数の中で停止してしまう。

正常なら、WM_NCMOUSEMOVE メッセージが到着することによって、関数から
戻って来るはずだと思われる。
0469396
垢版 |
2018/02/22(木) 10:45:19.02ID:9+xI5ulA
>>468
2つ目の WaitForMultipleObjectsEx() の中で停止してしまっていることが判明。
本来、先頭に Msg が付く方の API は、メッセージと HANDLE の両方を同時に
監視するようなコードでなくてはならないハズなのに、そうなってないらしい。
process_events() が、MotionNotify の XEvent が来ていたら WM_MOUSEMOVE
メッセージをキューにポストするらしいが、最初に一度だけ調べて XEvent が
来ていない場合は、以後、全く XEvent を調査せずに他の HANDLE 群の変化だけ
を待機してしまうらしい。

DWORD CDECL X11DRV_MsgWaitForMultipleObjectsEx( DWORD count, const HANDLE *handles,
                        DWORD timeout, DWORD mask, DWORD flags )
{
  DWORD ret;
  struct x11drv_thread_data *data = TlsGetValue( thread_data_tls_index );

  if (!data) {
    if (!count && !timeout) return WAIT_TIMEOUT;
    return WaitForMultipleObjectsEx( count, handles, flags & MWMO_WAITALL,
                     timeout, flags & MWMO_ALERTABLE );
  }
  if (data->current_event) mask = 0; /* don't process nested events */

  if (process_events( data->display, filter_event, mask )) ret = count - 1;
  else if (count || timeout) {
    ret = WaitForMultipleObjectsEx( count, handles, flags & MWMO_WAITALL,
                    timeout, flags & MWMO_ALERTABLE );
    if (ret == count - 1) process_events( data->display, filter_event, mask );
  }
  else ret = WAIT_TIMEOUT;

  return ret;
}
0470396
垢版 |
2018/02/22(木) 10:46:53.05ID:9+xI5ulA
【通常時、WM_MOUSEMOVE がキューへ追加される時の実行経路】

X11DRV_MsgWaitForMultipleObjectsEx()
--> process_events()
---> call_event_handler( Display *display, XEvent *event )
---> handlers[event->type]( hwnd, event );
event->type = MotionNotify (6)
---> X11DRV_MotionNotify( HWND hwnd, XEvent *xev )
XMotionEvent *event = &xev->xmotion;
INPUT input;
---> send_mouse_input( hwnd, event->window, event->state, &input );
---> __wine_send_input( HWND hwnd, const INPUT *input )
---> send_hardware_message( hwnd, input, 0 );
SERVER_START_REQ( send_hardware_message ) {
switch (input->type)
case INPUT_MOUSE:
}
---> wine_server_call( req );
---> DECL_HANDLER(send_hardware_message)
(send a hardware message to a thread queue)
---> queue_mouse_message( desktop, req->win, &req->input, req->flags, sender );
messages[MOUSEEVENTF_MOVE (1)] = WM_MOUSEMOVE
---> queue_hardware_message() : WM_MOUSEMOVE
list_add_tail( &input->msg_list, &msg->entry );
set_queue_bits( thread->queue, get_hardware_msg_bit(msg) );
--->
if (!always_queue || merge_message( input, msg )) free_message( msg );
else {
msg->unique_id = 0; /* will be set once we return it to the app */
list_add_tail( &input->msg_list, &msg->entry );
set_queue_bits( thread->queue, get_hardware_msg_bit(msg) );
}
0471396
垢版 |
2018/02/22(木) 10:47:48.18ID:9+xI5ulA
【WM_NCMOUSEMOVE は WM_MOUSEMOVE から修正される】
PeekMessageW() または GetMessageW() の中で、hittest が HTCLIENT 以外
の場合は、WM_MOUSEMOVE が WM_NCMOUSEMOVE に修正される :

PeekMessageW() または、GetMessageW()
---> peek_message()
---> switch(info.type)
   case MSG_HARDWARE :
---> process_hardware_message()
---> process_mouse_message() {
   if (hittest != HTCLIENT) {
    message += WM_NCMOUSEMOVE - WM_MOUSEMOVE;
    wparam = hittest;
   }
0472login:Penguin
垢版 |
2018/02/22(木) 15:16:45.32ID:9+xI5ulA
頭に Msg が付かない方の WaitForMultipleObjecstEx() も
挙動がおかしい。 timeout に 0 を指定しても、無限に
待機してしまうことがある。
0473396
垢版 |
2018/02/22(木) 16:31:11.62ID:9+xI5ulA
Msg が付く方の WaitXxxx で、usleep() で、0.3秒ほど停止しながら、
Polling (Loop) するようにしてから、FIXMEでメッセージを観察してみた。
普段は、usleep() 命令は、呼び出してから 0.3 秒で戻ってくる。
ところが、先日からの遅くなる条件でドラッグし続けた場合、単純な usleep() の
1命令が、呼び出してから、永久に戻ってこなくなる。ドラッグをやめると、
人間が長く感じるほどの長い時間が経過してから戻ってくる。

これは、WINEの問題ではなく、Linux Kernel の問題だろうか???
■ このスレッドは過去ログ倉庫に格納されています

ニューススポーツなんでも実況