これはかなーり恥ずかしい事なんじゃないかなと思いましたが:「DrawDib 関数群」の存在,さっき初めて知りました。DIB を DC に描画するとしたら通常は ::SetDIBitsToDevice() や ::StretchDIBits() を使うようですが,どうせ透過モード ( ::StretchDIBits() API 13 番目の引数 dwRop ) に SRCCPY しか用いないならば DrawDib 関数群を用いた方がパフォーマンス的によさげ。Video For Windows ライブラリとして提供されている API なだけあり,動画の再生に最も適したカタチのようです。
とりあえず少しだけかじってみました。大体こんな感じでよい模様。
#ifndef STRICT
#define STRICT
#endif // STRICT
#include <windows.h>
// 特別なヘッダファイルとライブラリが必要
#include <vfw.h>
#pragma comment( lib, "vfw32.lib" )
static HDRAWDIB g_hdd = 0;
int __stdcall WinMain( HINSTANCE hinst, HINSTANCE, LPSTR, int ) {
// よくわかんないけどハンドルを生成
g_hdd = ::DrawDibOpen();
HWND form = createMainForm( hinst ); // メインのウィンドウを生成する関数
MSG msg;
while ( ::GetMessage( &msg, 0, 0, 0 ) > 0 ) {
::TranslateMessage( &msg );
::DispatchMessage( &msg );
}
// よくわかんないハンドルを閉じる
::DrawDibClose( g_hdd );
return 0;
}
///// メインのウィンドウのプロシージャ /////
static
LRESULT CALLBACK form_methods( HWND this_, UINT msg, WPARAM wp, LPARAM lp ) {
static unsigned char *pixels = 0; // ビットマップのバッファ
static BITMAPINFOHEADER bmih = {0};
if ( msg == WM_PAINT ) {
PAINTSTRUCT ps;
HDC dc = ::BeginPaint( this_, &ps );
// DrawDibDraw() の使い方はこう
::DrawDibDraw(
g_hdd, // 第 1 引数 .. HDRAWDIB
dc, // 第 2〜6 引数 .. destination の設定
0,0,
bmih.biWidth, bmih.biHeight,
&bmih, // 第 7 引数 .. LPBITMAPINFOHEADER
pixels, // 第 8〜13 引数 .. source の設定
0,0,
bmih.biWidth, bmih.biHeight,
0 ); // 第 14 引数 .. wFlags -- よくわかんない
::EndPaint( this_, &ps );
return 0;
}
(以下略)
なんだか分かんないモノだらけですが,しばらくこれを研究してみようと思います。
ヤバい。私の C/C++ の理解の無さの異常さはマジ尋常じゃない。もう尋常じゃないなんてもんじゃない。超尋常じゃない。マジヤバい。
/// file_a.cpp ///
#include <iostream>
struct str { void method() { std::cout << "file A" << std::endl; } };
void func_A() { str s; s.method(); }
/// file_a.cpp ///
#include <iostream>
struct str { void method() { std::cout << "file B" << std::endl; } };
void func_B() { str s; s.method(); }
void func_A();
void func_B();
int main( int ac, const char *Av[] ) {
func_A();
func_B();
return 0;
}
file A file A
同名の構造体は外部リンケージを持ちますか? そういう仕様は聞いたことがない気が。おっかしいなあ・・・
こういうコードなら見慣れてるんですよね。
struct str { void method() { /*do something..*/ } };
#include "file_str.hpp"
void func_A() { 構造体 str を使う... }
#include "file_str.hpp"
void func_B() { 構造体 str を使う... }
普段はこのように構造体の宣言をヘッダファイルに追い出しています。なまじヘッダファイルではなく cpp ファイルに直に埋め込み,しかもメソッド void method() の中身を違えてしまったために,変に混乱してしまったのが上の雑記。
void method() の中身が違えば警告くらいは出して欲しいとも思うのですが,それはさすがに酷かとも。
「ブックマークはトップページに」と書かれてあるサイトは星の数ほどありまして,脊髄反射的に「どうしてブックマークの箇所をいちいち指示されねばならんのだ!私は断固として闘う戦う斗うぅ!今まさに此処でブックマークしてやるぅぁぅ!!」と息巻いたものの数ヵ月後に再び訪れてみると,案の定,移転してたりするわけです。ブックマークする時におとなしくトップページを覗いておけば,おそらく移転予告にも気付いただろうにね。
そんな失敗を何度か経験しているので,私は常にサイトの注意書きをチェックします。管理人さんの意志を充分に尊重し,私がこの雑記からリンクを貼る際にも気を使ってます ( ・・多分,今の所はあんまし問題ないかと・・ )。「ディープリンク禁止」には法的根拠はないばかりか「リソースの集合体」としてのインターネットの価値を薄めてしまうものですが,かと言って無用なゴタゴタを起こすのも面倒だし,むしろハッキリ言ってしまえば「厨房を敵に回すのは驚異的に面倒臭い」と。
さてさて,サイト内案内の目的でページ内に「戻る」という文字列を記述しておき,そこを辿る事でページの目次か或いはトップページへ戻れるようにしてある事があります。この雑記でも h1 要素より前に indexj.htm とブラクラチェッカーへのリンクをリストとして記述してあります。
時に,こんなものも:
<a href="javascript:history.go(-1)">戻る</a>
JavaScript の history.go() メソッドで直前のページに戻ります。殆どの場合「直前のページ」とはそのサイトのインデックスであるため,わりと問題にはなりません。
しかし検索エンジンから飛び込んで来た場合,なおかつ各ディレクトリを覗けない設定にされている場合,なおかつなぜかそのサイトのトップページが index.html でも default.html でもその他常識的なファイル名でもない場合,トップページどこよ?
ねえ。
( プログラマならば ) 誰でも一度は必ず経験する設計上のアクシデント「デッドロック」をナマでっていうかリアルで見た日にはもうおかず無しでご飯 3 杯は軽いですマジで。いや,最近「米」なるものをサッパリ口にしていませんが・・・
路上に 2 台の車が駐車していました。本当は駐車禁止ですよ?
片側からバスが,片側からトラックがやって来ました。
ぬ,なかなか不適切な関係っぽいですが,なんとかお互い通り抜けられるかな・・・
はぅぁっ,後続車がっ!?・・
お,ちょ,ちょっと・・・
・・あ〜あ・・どうすんの,これ・・
バスの後ろから来た車,トラックの後ろから来た車は,それぞれバスやトラックがそのまま直進するものと信じて疑っていません。なぜならば,バスやトラックの前に車が停まっているとは夢にも思っていないからですね。
残念ながら顛末を観察する暇はありませんでしたが,まぁなんとか解決したのかな? 流血事件に発展しやしないかと ワクw ドキドキでした。
API を整理したりメッセージを拡張したりして,実用に耐えられるコントロールが出来たんじゃないかなと思います。ああそういえば,グラデーションの色を変えたり,FINISH した時に自動的に破棄する仕組みも組み込んだんだけど,外部から設定できるようにはしてありません。
それぞれのメッセージや API の解説も作ろうかなと思ったけど,しかしこんなのを使うくらいなら自作した方が安心確実ですよね。ドキュメント化は,要望があれば〜という事で・・・あるかなあ?
いやー,実験しているわけですが・・そもそもこういう実験は,実験可能な状態に持っていくまでがちょっと面倒臭かったりします。
いまいち目から鱗が落ちる程度の発見がないわけですが,なんとなく分かった事:
SW_SHOWNA を行うと,前面に出てくるSW_SHOWNOACTIVATE を行うと,元の大きさに戻った上で前面に出てくるんー,まだ,なんていうか,こう,煮えきりません。
昨日の続きね。::CreateWindowEx() で生成できるような常識的なコントロールの場合,内部データはおそらく ::SetWindowLong( wnd, 0, (LONG)data ); を用いているはずです。GWL?USERDATA じゃなくてね。次のようなコードを書きました。
#define STRICT
#include <windows.h>
WNDPROC original = 0;
LRESULT CALLBACK proc( HWND wnd, UINT msg, WPARAM wp, LPARAM lp ) {
if ( msg == WM_CLOSE )
::DestroyWindow( wnd );
if ( msg == WM_DESTROY )
::PostQuitMessage(0);
return ::CallWindowProc( original, wnd, msg, wp, lp );
}
int main() {
HWND wnd = ::CreateWindowEx( 0, "EDIT", "test",
WS_OVERLAPPEDWINDOW | WS_VISIBLE,
CW_USEDEFAULT, CW_USEDEFAULT,
CW_USEDEFAULT, CW_USEDEFAULT,
NULL, NULL, ::GetModuleHandle(0), 0 );
original = (WNDPROC)::SetWindowLong( wnd, GWL_WNDPROC, (LONG)proc );
MSG msg;
while ( ::GetMessage( &msg, 0, 0, 0 ) ) {
::TranslateMessage( &msg );
::DispatchMessage( &msg );
}
return 0;
}
エディットコントロールをそのまま一つのウィンドウとして表示するバカコードです。これは何の問題もないのですが,ウィンドウを生成した後で次のようにすると
// 注意:このコードでは,アプリケーションが不正な処理を行います
::SetWindowLong( wnd, 0, 12 ); // 12 は適当
アプリケーションの起動時にすぐに「不正な処理」を行ってしまいます。つまり,少なくともエディットコントロールはこの部分を無視していない = おそらく内部データをここで保持しているであろう事が分かります。
実は下記の「にょっこりボックス」はこれを踏まえていませんでした。改造してみたいと思います。
最近の私は非常にというか異常に早起きなんですが,えー,3 時に寝まして目が覚めるのが 7 時なので睡眠時間 4 時間ですか。まじですか。
とんでもなくマズいので私をツボにはめた激安輸入コーヒーを飲みながら適当にウェブを巡回していると,ふと気づきました:「最近,睡眠時間が極端に短くなった〜」とか書いてる人が意外に多い事に。なぜ? 春ですよ? 春眠暁不覚ですよ?
私が推理するにこれは多分あれだ。タマさん ( アザラシ,3 歳 ) とぁゃιぃ関係が噂されている某kチガイ女史を倒して一躍ヒーローとなったスカラー・・わわわ,大量の黒装束の人たちが部屋のドアをバンバンしてまドキュソぐわっやられたぁ ( <- 楽しい )。
::ShowWindow() における SW_SHOWNA と SW_SHOWNOACTIVATE の違いをなんとかして観察したいんだけど,どうもうまい方法が見つからないですな。。。
ところで:一昨日の「にょっこりボックス」のような汎用コントロールを自作する場合,独自のデータを保持したい場合が多々あります。こんな時,該当のウィンドウハンドルとどう対応付ければいいんだろう? 列挙してみると
::SetWindowLong( hwnd, GWL_USERDATA, data ) を使う::SetWindowLong( hwnd, 0, data ) を使うstd::map<HWND, data_t*> で対応付ける3 番目は楽かも知れないけど,ちょっと "富豪的" にも程があるかも。また GWL_USERDATA は,最終的なユーザがこのコントロールをサブクラス化した場合にユーザ自身が保持しておきたいデータの格納場所に使いたくはありませんか? GWL_USERDATA の部分はその最終的なユーザに譲り,2 番目の方法を採るのが最適解かと。
実験してみました。つか,続きます。
コナミの名作と言って私が真っ先に挙げるのは「ツインビー」でも「ときめも」でもなく「グラディウス」ですが,さらにシリーズ中の「沙羅曼蛇」の名作っぷりは異常。そこそこマイナーなゲームなのに最近 i アプリ用のソフトウェアとしてリリースしているんでコナミの力の入れ所の意外さは異常。ついでに「炎の予言」の痛さも異常。
元々アーケードゲームとして存在していたらしく,友人がなぜかその「攻略ビデオ」 ( VHS ) を所持していました。ボス戦直前に流れる機械的な音声に聴かせられて,当時子供だった私はすっかりコナミのシューティングにハマったわけですが。
しかし音声が聞き取りにくいので歯痒い想いのまま過ごす事およそ 10 年,ある程度の英語力もついたところでもう一度聞いてみたのです。。。「えにー とぅーどぅーはず あだー とぅりーでぃー あぅわ ふぉーすふぃーよー」・・「Any to-do has, other 3-D, our forcefield.」? 「どんな to-do も持っている,他の 3 次元,私たちの力場」?謎の英語が出てきてしまいました。
今日偶然知った正解は「An intruder has penetrated our forcefield」だそうで。ああ,そう。。。
昨日の「にょっこりボックス」は非常に大人気でダウンロード数が 1 億を突破しました生産が追いつかない程ですありがとうっていうか嘘です。生産しないし。
「ユーザの邪魔にならない」というのが一つの目的ですが,しかし「絶対にユーザに気づかれない」のは困ります。注意を促すので,必ず一瞬だけでもユーザに確認してもらわなければなりません。このボックスを出現させるにあたり,こんなコードにしてみました。
::ShowWindow( this_, SW_SHOWNOACTIVATE );
うん,これで問題なく動きます。テキストエディタ等にフォーカスがある場合でも,そのフォーカスを奪う事なくボックスがひっそりと現れます。。。しかし 2002-10-02 で同じような事をやった時は,SW_SHOWNOACTIVATE ではなく SW_SHOWNA を使っているような気が ( ちなみに 2002-10-03 で「親ウィンドウが非アクティブ化してしまう = フォーカスが奪われてしまう」と騒いでいますが,2002-10-14 で解決しています )。
これはどう違うんだろう? ちょっと追及してみるべきですか奥さん?
Unicode Viewer 作製の勢いにまかせて,こーんなジョークプログラムを作ってみました。
->にょっこりボックス;
実行すると,デスクトップの右下からヘンなボックスがにょっこりと現れます。こんな感じ。
引数をつける事で,任意のメッセージを任意のタイミングで表示できます。実行されてから 10,000 ミリ秒後に "ネタにマジレス,してませんか?" というボックスを出現させるには,こう:
nyoko.exe -d10000 ネタにマジレス,してませんか?
ユーザに何かを知らせたい場合の常套手段は「メッセージボックス」ですが,メッセージボックスを閉じるにはユーザのアクションが必要です ── OK ボタンをクリックしたりね。それだけ重要なメッセージであればともかく,“ちょっとお知らせ”程度のメッセージの場合は「自動的に現れて,ユーザがそれに気づいたかどうかに関わらず,自動的に閉じる」方が便利。ユーザの作業を邪魔せずに“ちょっとお知らせ”する事が出来ます。
私が知る限りでは,例えば MSN メッセンジャーや Mozilla 付属のメーラーがこれを採用しているようです。他のアプリケーションでも,もっとスマートなモノがあったりするのかな?
適当にフォントで遊びながら少しずつ機能を追加して行ったら,何時の間にかここまで出来てしまいました。あれれー?
ただユニコードの一覧を見るだけのアプリケーションですが,文字をクリックすると例えば "Ӓ" のような実体参照文字列をクリップボードにコピーできるようにしてみました。・・ていうか,先生! 使い道が分かりません!
なお,メニューの "Appendix" は文字通りオマケです。マシンにインストールされているフォントと詳しいレポートを列挙しています。
で,次は何を作ろうかな。。。
昨日のコードでは LOGFONT::lfFaceName しか表示させていません。そこで,こんなコードを書いてみると
#include <windows.h>
#include <iostream>
static
int CALLBACK enumFontProcedure(
ENUMLOGFONTEX *pelf, NEWTEXTMETRICEX *pntm,
DWORD fonttype, LPARAM lp )
{
std::cout << (char*)pelf->elfLogFont.lfFaceName
<< " / " << (char*)pelf->elfFullName
<< " / " << (char*)pelf->elfStyle
<< " / " << (char*)pelf->elfScript << std::endl;
return 1;
}
int main( int ac, const char *av[] ) {
HDC dc = ::GetDC( HWND_DESKTOP );
LOGFONT enums = {0};
enums.lfCharSet = DEFAULT_CHARSET;
::EnumFontFamiliesEx( dc, &enums, (FONTENUMPROC)enumFontProcedure, 0, 0 );
::ReleaseDC( HWND_DESKTOP, dc );
return 0;
}
こんな出力が。※ これでもかなり略しています
Arial / Arial / Regular / 欧文 Arial / Arial / Regular / ヘブライ語 Arial / Arial / Regular / アラビア語 Arial / Arial / Regular / 中央ヨーロッパ言語 Arial / Arial / Regular / キリル言語 Courier New / Courier New / Regular / 欧文 Courier New / Courier New / Regular / トルコ語 Courier New / Courier New / Regular / バルト言語 Courier New / Courier New / Regular / 中央ヨーロッパ言語 Lucida Console / Lucida Console / Regular / 欧文 Lucida Console / Lucida Console / Regular / 中央ヨーロッパ言語 Lucida Console / Lucida Console / Regular / キリル言語 Times New Roman / Times New Roman / Regular / 中央ヨーロッパ言語 Times New Roman / Times New Roman / Regular / キリル言語 Wingdings / Wingdings / Regular / シンボル Symbol / Symbol / Regular / シンボル
一番右端に出力した項目 = ENUMLOGFONTEX::elfScript に注目。他の項目に変化はないものの,ここが違うために同じ facename がいくつか列挙されるわけですか。
何にしても Unicode Viewer は,LOGFONT::lfFaceName に設定するための facename 以外に用はありません。重複を防ぐために std::map<string,int> を使うのは非常にスマートなやり方。
#include <windows.h>
#include <iostream>static
#include <string>
#include <map>
#pragma warning( disable: 4786 )
static std::map<std::string,int> unique_check;
int CALLBACK enumFontProcedure(
ENUMLOGFONTEX *pelf, NEWTEXTMETRICEX *pntm,
DWORD fonttype, LPARAM lp )
{
char *facename = (char*)pelf->elfLogFont.lfFaceName;
if ( unique_check[ facename ] == 0 ) {
unique_check[ facename ] = 1;
std::cout << facename << std::endl;
}
return 1;
}
int main( int ac, const char *av[] ) {
HDC dc = ::GetDC( HWND_DESKTOP );
LOGFONT enums = {0};
enums.lfCharSet = DEFAULT_CHARSET;
::EnumFontFamiliesEx( dc, &enums, (FONTENUMPROC)enumFontProcedure, 0, 0 );
::ReleaseDC( HWND_DESKTOP, dc );
return 0;
}
こんな感じでリストを生成し,コンボボックスに登録してやればよいようです。
ちなみに #pragma で警告 4786 を出さないようにするはとっても重要。テンプレートの引数が長くなってしまうようで,これがなければ大量の warning に埋め尽くされます。
近頃 "Natrue Made Calcium" を摂取し始めました。なかなか貧乏生活が長引き,思うように栄養が摂取できないのです。しかしプログラマたるもの常に精神を安定させていなきゃならんので,カルシウムはどうしても欠かせません。虫歯になって煩わしい思いをするのもプログラマにとっては致命的。
検索すると "Nature Made Calcium and Magnecium with Zinc" という製品が目につきます。私が購入したのは "Calcium with Vitain D"。別バージョンみたいな感じのようです。カルシウムを効果的に体内に吸収するにはビタミン D も必須。ただしビタミン D は日光を浴びる事で皮膚表面で生成されるので,あまり目くじら立てて摂取するものでもなさげ。マグネシウムと亜鉛はカルシウムの吸収に関係はなさそうですが,調べてみたところ「現代人に足りていない 3 大ミネラル」だそうで。
他社からも似たような製品が生産・販売されていますが,なぜか "Nature Made Calcium" が飛躍的に安いのでオススメの一品です。お金をかけずにピンポイントでカルシウムを摂取するには異常に便利。
・・で,ぁゃιぃ掲示板で「リタリンをキメた (´Д`)y-~~」とかぁゃιぃクスリをキメている人たちの中で,自分も書き込むわけですよ。「カルシウム 300mg をキメた (´Д`)y-」とね。・・・明らかに浮いてるぞ自分。
半年ほど前,2002-09-15 の周辺でフォントについていろいろ研究していますが,どうもいまいち遊びこなせていないというか。。。
で,今日は気合を入れて遊びました。フォント列挙には EnumFonts() ではなく EnumFontFamiliesEx() を用いて,こんなコード:
#include <windows.h>
#include <sstream>
#include <list>
using namespace std;
static list<string> fonts_list_;
static
int CALLBACK enumFontProcedure(
ENUMLOGFONTEX *pelf, NEWTEXTMETRICEX *pntm,
DWORD fonttype, LPARAM lp )
{
ostringstream ost;
const char *facename = (char*)pelf->elfLogFont.lfFaceName;
const char *fullname = (char*)pelf->elfFullName;
const char *style = (char*)pelf->elfStyle;
const char *script = (char*)pelf->elfScript;
ost << facename;
fonts_list_.push_back( ost.str() );
return 1;
}
list<string> *getFontsList( HDC dc ) {
if (!dc) return &fonts_list_;
fonts_list_.clear();
LOGFONT enums = {0};
enums.lfCharSet = DEFAULT_CHARSET;
enums.lfFaceName[0] = 0;
enums.lfPitchAndFamily = 0;
::EnumFontFamiliesEx( dc, &enums, (FONTENUMPROC)enumFontProcedure, 0, 0 );
fonts_list_.sort();
return &fonts_list_;
}
得られたフォント情報のうち LOGFONT::lfFaceName をリストに登録し,list::sort() でソートを行います。これをコンボボックスに登録すると,こうなりました:
うわー,なんかいっぱい出てるし? ( つづく )