Версия для печати темы
Нажмите сюда для просмотра этой темы в оригинальном формате
Форум программистов > C/C++: Системное программирование и WinAPI > отловить отпускание левой кнопки мышки


Автор: nns2009 6.2.2009, 23:34
Я заметил одну вещь:
WM_LBUTTONUP посылается окну только если мышка отпущена в его области(на окне),
а мне нужно как-то узнавать что мышка отпущена, где бы она ни была.
Подскажите, как это сделать?

Автор: SenkraD 6.2.2009, 23:51
nns2009, вы правила читали? Поиска нет или как?
Я по форуму щас не искал, но google дал http://forum.sources.ru/index.php?showtopic=238248&st=0&#entry1970512, http://en.allexperts.com/q/C-1040/Low-Level-Mouse-Hook.htm и http://www.codeproject.com/KB/DLL/hooks.aspx

Автор: nns2009 7.2.2009, 21:12
Спасибо за ссылки, но правила я читал(и если б я не пользовался поиском, то моих сообщений было в два раза больше).
Ставить ловушки слишком заумно, а в моём случае буду считать, что если пользователь увёл мышку за пределы окна, то он её отпустил,
в моём приложении это не слишком принципиально.
Буду искать нужное событие.

Автор: GremlinProg 7.2.2009, 22:30
Цитата(nns2009 @  7.2.2009,  23:12 Найти цитируемый пост)
в моём случае буду считать, что если пользователь увёл мышку за пределы окна, то он её отпустил

т.е. WM_LBUTTONDOWN, все таки происходит в твоем приложении, т.е. тебе для своего приложения  нужно узнавать что мышка отпущена?

если так, то, хуки здесь не нужны, достаточно использовать механизм "захвата мыши": SetCapture и ReleaseCapture

После вызова SetCapture, все мышиные сообщения начнут приходить в твое окно до тех пор, пока мышь не будет выпущена другим SetCapture или ReleaseCapture, независимо от того, проходит курсор по нему или нет.

подробнее - смотри в поиск по этим ключам, тема не новая

Автор: Earnest 9.2.2009, 16:26
Capture - это слишком мощно, его захват "портит" логику многих процессов, иак что лучше не использовать всуе...
Разве что это свяано с тащением чего-либо или типа того, тогда да, Capture. В противном случае вопрос, насколько синхронно тебе нужно знать, что мышка вышла за пределы окна\отпустила кнопку. Если  это нужно "не очень синхронно" - то поставь таймер и проверяй, не вышла ли мышь за пределы окна. Строго говоря, ставить таймер нужно, только если мышь приползла в окно (т.е. по первому MouseMove), а как только ты увидел (по таймеру), что мышь уползла - снимай. Диапазон подбери органолептически, чтобы хорошо смотрелось...
Ну а если нужно синхронно - то только хук. И почему, собственно, хук слишком заумный?

Автор: GremlinProg 10.2.2009, 14:08
ну, тут я уже просто предполагаю, что nns2009, организует какую-то сенсорику на залипании мыши, поэтому и предложил механизм захвата, конечно, если нужно только определяты выход за пределы клиентской/неклиентской области, то захват не нужен, как, впрочем, и таймер, поскольку этот механизм реализован в системе: http://msdn.microsoft.com/en-us/library/ms646265(VS.85).aspx + WM_MOUSELEAVE/WM_NCMOUSELEAVE

Добавлено @ 14:15
хук, кстати, не слишком заумный, это крайний механизм перехвата всего и вся, здесь-то он зачем, когда можно обойтись более дипломатично, тем более, хук - узкое горлышко, ничего не стоит его просто перегрузить, в обоих смыслах ))

Автор: Earnest 10.2.2009, 14:43
Цитата(GremlinProg @  10.2.2009,  15:08 Найти цитируемый пост)
TrackMouseEvent + WM_MOUSELEAVE/WM_NCMOUSELEAVE

Это дело, к сожалению, звучит соблазнительно, но работает так себе. Во всяком случае, при тестировании мне довольно быстро удавалось получить ситуацию, когда MOUSELEAVE не приходил - просто быстро двигая мышкой. И я на этот способ забила. А таймер работает железно (ну, почти...) Впрочем, должна оговориться: дело было давно, уж не помню в каком виндоус. Возможно, сейчас этот способ работает стабильнее. Но осадок остался... smile 

Автор: GremlinProg 10.2.2009, 15:33
Цитата(Earnest @  10.2.2009,  16:43 Найти цитируемый пост)
Это дело, к сожалению, звучит соблазнительно, но работает так себе

да нет, нормально работает, о замечаниях к TrackMouseEvent я уже писал, правда давно, но, тем не менее, работает она до тех пор, пока очередь сообщений потока, владеющего окном, не пуста

это ключевой момент

ладно, добью таки до конца уже начатое, чтобы было понятно, когда необходимо дополнить любые сенсорные проверки на мышь
кстати, это касается не только TrackMouseEvent, так же, любые проверки на WM_NCHITTEST или просто на WM_MOUSEMOVE, в общем - на любые проверки, которые производятся в ответ на оконные сообщения, зависимые от внешних устройств

вот, к примеру, главный цикл окна:
Код

while (GetMessage(&msg, NULL, 0, 0)) 
{
    if (!TranslateAccelerator(msg.hwnd, hAccelTable, &msg)) 
    {
        TranslateMessage(&msg);
        DispatchMessage(&msg);
    }
}

что он делает? GetMessage - это "ядерная" функция, которая "спит" до тех пор, пока очередь пустая, т.е. этот вариант для TrackMouseEvent неудобен, поскольку нужное нам время GetMessage отдает другим процессам
ок, теперь другой вариант:
Код

do{
    fPeek    = ::PeekMessage(&m_Msg,0,0,0,PM_REMOVE);
    if(fPeek == TRUE){
        if(!::TranslateAccelerator(m_TranslateWindow,m_Accel,&m_Msg)){
            ::TranslateMessage(&m_Msg);
            ::DispatchMessage(&m_Msg);
        } 
    }else if(fPeek == FALSE){
        //    время Idle
        ::WaitMessage();
    }else{
        //    обработать ошибку
    }
}while(m_Msg.message != WM_QUIT);


PeekMessage - это более гибкий вариант GetMessage, который не отдает автоматом время другим процессам, вместо этого, она возвращает TRUE, если сообщение было успешно извлечено из очереди, и FALSE - когда извлекать из очереди просто нечего, она пустая
при успешном возврате из PeekMessage, мы делаем то же самое, что и в примере выше, т.е. это обычная обработка сообщения, а вот в случае, когда PeekMessage возвращает FALSE, как раз и наступает время простоя(Idle), которое мы теперь можем использовать по своему

есть 2 варианта
1. самим отдать время простоя в систему, т.е. дать возможность поработать другим задачам в этот период
2. сделать не слишком долгосрочную обработку чего-либо, пока сообщения в очереди вновь не появятся

WaitMessage - первый вариант, эта функция будет просто "спать", пока очередь не активируется
второй вариант - как раз то, что нам и нужно, т.е. мы сами можем здесь поставить необходимую нам обработку (допустим проверку текущих координат курсора на предмет присутствия его в нужной нам области и, в случае отсутствия - снять какие-либо флаги состояния, или напрямую вывести компонент в неактивный режим, вобщем, это уже непосредственно модель простоя, которую программист может реализовывать по своему вкусу и предубеждениям), после чего, если более время Idle нам не нужно, вызвать WaitMessage

если не вызывать WaitMessage, или нагружать слишком много проверок в этот момент, то нагрузка на процессор будет 100% на протяжении всей работы приложения, так что не стоит этим злоупотреблять, поскольку обычная работа приложений уже будет нарушена + начнут вылезать различные артефакты, которым не было места при нормальной работе

ну вот, теперь, похоже добил )

Автор: Earnest 11.2.2009, 09:02
GremlinProg, спасибо, интересная информация. Насчет того, что очередь сообщений не должна быть пуста, не знала. Я пыталась использовать TrackMouseEvent с MFC, а там свой цикл выборки сообщений (тоже PeekMessage + Idle-обработка).
Вмешаться, конечно, можно, но тогда я так далеко не влезала.
 

Автор: GremlinProg 11.2.2009, 12:26
ну, вобщем-то ничего нового я не открыл, на сколько я знаю, все средства разработки, будь то Delphi, MFC, WTL и т.п. - все предоставляют возможность обрабатывать время простоя, просто информации по поводу "как его использовать?" не слишком много. Большинство считают, что это аппендикс, который нужен только для расширения функционала программы

наверняка, каждый видел это магическое OnIdle с булевым параметром Done, так вот Done - как раз признак завершения времени простоя, который обычно упрощено раскрывается так:
Код

BOOL Done;
do{
  Done = TRUE;
  OnIdle(&Done);
}while(!Done);
::WaitMessage();

OnIdle - тут либо глобальный метод, либо перебор всех окон потока с передачей флага в объектной модели методу OnIdle
но вот здесь как раз и лежит подводный камень:
флаг, сам по себе несет только признак, поэтому, если его передавать нескольким обработчикам (в цепочке простоя), реально, он может отражать только признак завершения для последнего, а не для всех.
отсюда могут возникать неприятности при бузусловной установке флага (те самые промахи, при быстром движении мыши), поскольку
Код

Done = (::PtInRect(...) == FALSE);

и
Код

if(::PtInRect(...) == FALSE){
  Done = TRUE;
}

совершенно разные логические манипуляции
в цепочке OnIdle, такого рода, нельзя использовать первый вариант, только второй - условная установка флага.
Ситуацию можно легко поправить, если Done будет счетчиком Repeat:
Код

int Repeat = 0;
do{
  OnIdle(&Repeat);
}while(Repeat);
::WaitMessage();

Код

if(::PtInRect(...) != FALSE){
  ++Repeat;
}

и логически - понятно и соблазна поискать другой путь для решения, не возникает
но я ни разу не встречал в onIdle явного счетчика, видимо из-за накладных расходов по документированию, благо, c WinAPI мы можем эти условности опустить и делать то, что нам действительно нужно.

Powered by Invision Power Board (http://www.invisionboard.com)
© Invision Power Services (http://www.invisionpower.com)