Показать все 5 сообщений этой темы на одной странице |
HLFX.Ru Forum (https://hlfx.ru/forum/index.php)
- Технические вопросы (https://hlfx.ru/forum/forumdisplay.php?forumid=20)
-- WM_ACTIVATE considered harmful (https://hlfx.ru/forum/showthread.php?threadid=5462)
WM_ACTIVATE considered harmful
Давайте проведем мысленный эксперимент.
Предположим, что вы, наслушавшись рассказов старших товарищей о некогда великом программисте Кармаке, вдохновились и решили написать свой шутер от первого лица. Вооружившись туториалами, вы справились созданием окна и инициализацией апи, и вот уже на экран выводится ваш первый в жизни треугольник.
Что еще нужно для полноценного шутера? Конечно же возможность вертеть головой при помощи мыши! Как это сделать? Правильный ответ на этот вопрос в наше время - используя WM_INPUT, но предположим на секунду, что вы по каким-то лишь вам одним ведомым причинам отвергли этот вариант, и решили реализовать mouselook также, как это было сделано например в Quake, потому что только в нем и живет дух старой школы.
Метод следующий:
1) Находим координаты центра экрана (окна)
2) Перемещаем указатель мыши в центр экрана
3) Периодически получаем текущие координаты указателя, считаем дельту и возвращаем указатель в центр
4) По вкусу устанавливаем ClipCursor/SetCapture, но это уже детали...
Вау! Гордясь собой, вы перемещаетесь по созданному вами миру, стремясь осмотреть созданный вами треугольник со всех сторон. Но тут вы услышали уведомление - это друг написал вам в инстаграмчике, приглашая на новый митинг Навального. Вы жмете Alt+Tab чтобы переключиться на браузер, и тут понимаете, какая с вами произошла неприятность - кровавая гэбня захватила ваш к... указатель мыши все время перемещается в центр экрана, даже когда вы работаете с другим окном!
Слегка отойдя от шока, вы понимаете, что вам нужно каким-то образом узнавать, когда пользователь переключился на игру (и тогда активировать работу с мышью) и когда он переключился на что-то другое (и тогда мышь деактивируется). Пошерстив MSDN (или как вариант - код кваки) вы узнаете о существовании сообщений WM_ACTIVATE и WM_ACTIVATEAPP. Они немного различаются, но для однооконного приложения, каким является типичная игра, разницы практически нет (хотя WM_ACTIVATE позволяет еще узнать о том, что окно свернуто).
Казалось бы - вот оно решение! Пользователь нажимает на окно - оно активируется, нажимает на другое - первое деактивируется. Все просто, не так ли?
К сожалению, нет. Вернее, не совсем...
Предположим, что вы долго работали над вашей игрой, и дошли до того момента, когда между запуском экзешника и появлением окна проходит пара секунд. Вы в очередной раз запустили вашу игру, но сразу после запуска экзешника случайно щелкнули по открытому окну папки, в которой экзешник располагался. Когда окно вашей игры наконец появилось, вы с удивлением обнаружили, что окно папки теперь находится поверх окна игры, а над ним бегает, перемещаясь в центр, указатель мыши. Конечно, это легко лечится альт-табом на окно игры, но все-таки сложно назвать это дружелюбием к пользователю...
Но постойте, ведь активация мыши завязана на WM_ACTIVATE, как же так получилось, что окно получило это сообщение, несмотря на то, что не является активным?
Все дело в том, что в многозадачной винде статус активного (active) окна имеет смысл только для треда, который это окно создал. Он ничего не говорит о том, имеет ли окно на самом деле фокус ввода в системе. Окно может быть активным в рамках своего треда, но не иметь при этом системого фокуса ввода. Самое гадкое при этом, что функции GetActiveWindow() и GetFocus() при этом будут вас убеждать что именно ваше окно, а не какое-то там другое является активным и имеет фокус.
Окно, имеющее системный фокус, называется foreground window, узнать, имеет ли ваше окно фокус можно используя функцию GetForegroundWindow(). У нее есть и собрат - функция SetForegroundWindow(). Так вот же оно - решение! Просто попросим систему дать фокус нашему окну...
К сожалению, в современных версиях винды приложение не может принудительно установить фокус ввода на свое окно. Подробнее об этом можно прочитать в документации к SetForegroundWindow.
Как же быть? Хороший вопрос...
Вам может придти в голову идея установить вашему окну стиль WS_EX_TOPMOST чтобы оно появлялось поверх всех остальных окон. Не делайте этого ни в коем случае! То, что ваше окно находится поверх всех остальных еще не говорит, что оно является foreground window! Если бы окно нашей гипотетической игры в приведенном выше примере имело этот стиль, то нажатие например Alt+F4 после появления окна игры, закрыло бы не окно игры, а то окно, которое вы нажали в последний момент перед появлением окна игры, например - чатик с Навальным...
Можно при получении WM_ACTIVATE дополнительно проверять GetForegroundWindow() но тогда при клике на окно вы не получите второй раз WM_ACTIVATE - окно-то уже активно! А имеет ли оно реальный фокус - кому какое дело...
Вообще существует два прямо противоположных взгляда на взаимодействие игры с системой:
1) Концепция старой школы предписывает игре считать себя главным и единственным куском кода в системе. Она предполагает агрессивный захват ввода, вывода и всех остальных ресурсов компа, рекомендацию закрыть все остальные приложения итд. Зависло? Ресет!
2) Более современная концепция предписывает игре считать себя таким же приложением как и все другие, уважать многозадачность и интересы пользователя. Зависло? Приносим глубочайшие извинения!
Сталкивались ли вы с приведенными выше проблемами? Как вы их решили? Есть ли в винде сообщение, которое посылается, когда приложение в действительности получает фокус ввода? Еще я заметил, что некоторые игры все-таки игнорят текущий фокус и запускаются поверх всего несмотря ни на что даже в современной винде. Как правило, это игры используещие D3D в полноэкранном режиме. Получается, что это отдельная фича в D3D, которая позволяет выкинуть такой фокус? Можно ли это как-то провернуть с обычным окном или на OpenGL?
Ты практически мою жизнь описал
SetForegroundWindow( host.hWnd ); |
SetFocus( host.hWnd ); |
__________________
My Projects: download page
F.A.Q по XashNT
Блог разработчика в телеграме
C:\DOCUME~1\C4C5~1\LOCALS~1\Temp\a33328if(72) : see declaration of 'size_t'
__________________
My Projects: download page
F.A.Q по XashNT
Блог разработчика в телеграме
C:\DOCUME~1\C4C5~1\LOCALS~1\Temp\a33328if(72) : see declaration of 'size_t'
Временная зона GMT. Текущее время 10:29. | Показать все 5 сообщений этой темы на одной странице |
На основе vBulletin версии 2.3.0
Авторское право © Jelsoft Enterprises Limited 2000 - 2002.
Дизайн и программирование: Crystice Softworks © 2005 - 2024