воскресенье, 26 января 2014 г.

ШИНAПN и бездны legacy

Есть у меня небольшой проект, по историческим причинам не использующий ни Qt, ни GTK, ни чего-либо другого. Только WinAPI или общение с WM через X11 под линем. И,  хотя X11 не лишен недостатков,  не буду оригинальным - WinAPI хуже его раз в стопятьсот.

Это не относится к новомодному интерфейсу  Metro и его API - честно говоря, не трогал и не имею желания. Просто потому что тайловые WM давно уже изобрели, и я нахожу подобные закосы либо шагом назад, либо желанием выпендриться шагом назад.

Сейчас Windows и WinAPI не ругал только ленивый, см.  [1] [2] [3]  и в этом я особо не оригинален. По последней ссылке, кстати есть некоторый список конкретных недостатков. Однако, мне довелось встретиться с несколько другими, значительно более раздражающими, и этот пост будет посвящен им. Замечу, что все они решаемые, но красоте коду они не прибавляют.

В первую очередь, SetWindowLong  с GWL_STYLE - это просто хаос и угар. Некоторые комбинации его флагов могут реально поломать окно ко всем чертям, в зависимости от версии Windows. К примеру, есть популярный хак вида

SetWindowLong(handle, GWL_STYLE,  oldstyle & ~WS_THICKFRAME);

Он запрещает пользователю изменять  размеры окна (почему вообще нет функции для этого?). В Windows 8 он ломает работу glViewport из-за того, что клиентская область начинает вычисляться неверно. И да, рамку она не убирает. Но более того - некоторые безобидные комбинации флагов стилей в вызове этой функции приводят к тому, что окно просто превращается в КРОВЬ КИШКИ МЯСО.

Ещё в недрах WinAPI есть замечательное событие WM_MOUSELEAVE . Ну, думается, если есть WM_MOUSEMOVE, который работает всегда и ловит перемещение мыши - оный должен работать также. Ничего подобного, если не вызвать TrackMouseEvent - сообщений не будет. Конечно, в X11 флаги перемещения мыши, входа и выхода из клиентской области задаются отдельно. НО! Ни один из них не включен по умолчанию.

Определённое неудобство может доставить  функция PeekMessage . Обратите внимание на второй аргумент её. В него должен передаваться дескриптор окна, для которого получается сообщение. Так вот, в него всегда надо передавать NULL. Иначе у вас не будет работать смена раскладки окна. Проверял на Windows XP, 7, 8 - нигде это не работает. Вообще, если сообщение всегда получается для окна текущего потока, имеет смысл задать себе - как часто у вас несколько окон обрабатывается в одном потоке?
UPD:  Оказывается в этом есть логика, см. статью.  И она тесно связана со следующим фактом, а именно...

CreateWindow, который создает не только окна, но и двери, шпаклевки, кирпичи, натяжные потолки - это классика нарушения принципа единственной обязанности и кроме как легаси не объясним.

Подводя итог, хочу сказать - используйте Qt, GTK, JUCE,  whatever. А WinAPI - нет.