Зря Вы так. Мне за код не стыдно. Там всего строк двести, но там есть и многопоточность, и перехват событий через SetWindowsHookEx. Код лаконичен, как и статья, но не обфусцирован, комментарии на месте. Привожу код ниже (убрал десяток закомментированных строк из того, что на гитхабе лежит).
Скрытый текст
#include <Windows.h>
#include "resource.h"
#include <process.h>
HHOOK handle;
volatile int sound_playing = 0;
wchar_t wbuffer[256];
int hour_iwillbeback(20); // час, когда я вернусь
int flag_iwillbeback = 0;
// имя файла со звуком, например, "пять часов утра..." для hours[5]
const wchar_t* hours[24] =
{
L"h0.wav", L"h1.wav", L"h2.wav", L"h3.wav", L"h4.wav", L"h5.wav",
L"h6.wav", L"h7.wav", L"h8.wav", L"h9.wav", L"h10.wav", L"h11.wav",
L"h12.wav", L"h13.wav", L"h14.wav", L"h15.wav", L"h16.wav", L"h17.wav",
L"h18.wav", L"h19.wav", L"h20.wav", L"h21.wav", L"h22.wav", L"h23.wav"
};
// единицы минут, например, "...восемь минут". "m0.wav" содержит слово "ровно".
const wchar_t* mins[20] =
{
L"m0.wav", L"m1.wav", L"m2.wav", L"m3.wav", L"m4.wav", L"m5.wav",
L"m6.wav", L"m7.wav", L"m8.wav", L"m9.wav",
L"m10.wav", L"m11.wav", L"m12.wav", L"m13.wav", L"m14.wav", L"m15.wav",
L"m16.wav", L"m17.wav", L"m18.wav", L"m19.wav"
};
// десятки минут, например, "сорок".
const wchar_t* mins10[4] =
{
L"m20.wav", L"m30.wav", L"m40.wav", L"m50.wav"
};
//===================================================================
// Произносит часы и минуты из структуры SYSTEMTIME
//===================================================================
void PlayTime(SYSTEMTIME st)
{
// ...надцать часов...
PlaySound(hours[st.wHour], NULL, SND_FILENAME);
if (st.wMinute < 20) // минуты в начале часа
{
PlaySound(mins[st.wMinute], NULL, SND_FILENAME);
}
else // от 20 до 59 минут
{
// Здесь сначала десятки минут
PlaySound(mins10[st.wMinute / 10 - 2], NULL, SND_FILENAME);
// теперь единицы минут, если они отличны от нуля
if (st.wMinute % 10 != 0)
PlaySound(mins[st.wMinute % 10], NULL, SND_FILENAME);
else
PlaySound(L"minut.wav", NULL, SND_FILENAME);
}
}
//====================================================================================
// Поток для проигрывания звука
//====================================================================================
static volatile bool flag_shutdown_thread = false; // флаг информирует поток о том, что нужно выключиться
volatile HANDLE h_snd_event = NULL; // Событие с автосбросом
uintptr_t snd_thread_handle = NULL;
unsigned __stdcall FuncSndThread(void* p)
{
while (true)
{
WaitForSingleObject(h_snd_event, INFINITE);
// Это сигнал к продолжению или к завершению?
if (flag_shutdown_thread) break;
// Живём и играем!
SYSTEMTIME st;
GetLocalTime(&st); // Получает текущее локальное время
// Московское время...
PlaySound(L"mos_vremya.wav", NULL, SND_FILENAME);
PlayTime(st);
if (flag_iwillbeback)
{
PlaySound(L"iwillbeback.wav", NULL, SND_FILENAME);
PlaySound(hours[hour_iwillbeback], NULL, SND_FILENAME);
}
}
return 0;
}
//====================================================================================
// Перехватчик правой кнопки мыши в любом месте экрана
//====================================================================================
LRESULT CALLBACK HookProc(int disabled, WPARAM wParam, LPARAM lParam)
{
if (!disabled)
{
MOUSEHOOKSTRUCT* pMouseStruct = (MOUSEHOOKSTRUCT*)lParam;
if (pMouseStruct != NULL)
{
if (WM_RBUTTONDOWN == wParam)
{
//Beep(450,100); // Для отладки
SetEvent(h_snd_event);
return 1;
}
if (WM_RBUTTONUP == wParam) return 1;
}
}
return CallNextHookEx(NULL, disabled, wParam, lParam);
}
//===================================================================
// Единственный диалог программы
//===================================================================
static BOOL CALLBACK DlgWndProc(HWND hdwnd,
UINT uMsg,
WPARAM wparam,
LPARAM lparam)
{
switch (uMsg)
{
case WM_COMMAND:
switch (LOWORD(wparam))
{
case IDCANCEL: // Не случилось
EndDialog(hdwnd, 2);
return 1;
case IDOK:
// второй поток ждёт этого события
SetEvent(h_snd_event);
return 1;
case IDC_COMBO_IWILLBEBACK:
hour_iwillbeback = SendDlgItemMessage(hdwnd, IDC_COMBO_IWILLBEBACK, CB_GETCURSEL, 0, 0L);
return 1;
case IDC_CHECK_IWILLBEBACK:
if (BST_CHECKED == SendDlgItemMessage(hdwnd, IDC_CHECK_IWILLBEBACK, BM_GETCHECK, 0, 0))
flag_iwillbeback = 1;
else
flag_iwillbeback = 0;
return 1;
} // switch WM_COMMAND
break; // if WM_COMMAND
case WM_INITDIALOG:
SetWindowPos(hdwnd, HWND_TOP, 50, 50, 0, 0, SWP_NOSIZE | SWP_NOREDRAW);
// заполняем часы в выпадающем списке
for (int i = 0; i < 24; i++)
{
_itow_s(i, wbuffer, 10);
SendDlgItemMessage(hdwnd, IDC_COMBO_IWILLBEBACK, CB_ADDSTRING, 0, (LPARAM)(wbuffer));
}
// по умолчанию вернусь в 20 часов
SendDlgItemMessage(hdwnd, IDC_COMBO_IWILLBEBACK, CB_SETCURSEL, (WPARAM)hour_iwillbeback, 0);
return 1; // Да, ставь фокус куда надо
break;
} // switch uMsg
return 0;
}
//=======================================================================
// программа
//=======================================================================
int WINAPI WinMain(HINSTANCE hInst, HINSTANCE, LPSTR cline, INT)
// Командную строку не обрабатываем
{
// Запускаем поток, реагирующий на событие
h_snd_event= CreateEvent(0, FALSE, FALSE, 0);
snd_thread_handle = _beginthreadex(NULL, 0, FuncSndThread, 0, 0, NULL);
// перехватываем нажатие на правую кнопку мыши во всей системе
handle = SetWindowsHookEx(WH_MOUSE_LL,
HookProc,
GetModuleHandle(NULL),
NULL);
DialogBox(hInst, MAKEINTRESOURCE(IDD_DIALOG1), NULL, (DLGPROC)DlgWndProc);
// перестаём реагировать на правую кнопку мыши
UnhookWindowsHookEx(handle);
// Выключаем второй поток
flag_shutdown_thread = true;
SetEvent(h_snd_event);
// Для очистки совести дождёмся окончания потока?
if (snd_thread_handle) WaitForSingleObject((HANDLE)snd_thread_handle, 0);
CloseHandle(h_snd_event);
return 0;
}
К сожалению, Алиса не разбирает тихий голос и речь после инсульта. А так - да, это мне сразу посоветовали. Если бы к ней можно было бы кнопку подключить на такой запрос - цены бы ей не было. Возможно, я просто не знаю, а возможность такая есть.
Понимаю Ваши возражения. Ждал, что мне скажут, что надо было всё делать на микроконтроллере. Но реальность такова, что старый нетбук валялся без дела, а написание программы заняло несколько часов. Time-to-market пересилило остальные аргументы.
Я имел в виду не просто раскладушку, а раскладушку с кнопками быстрого набора. Кроме того, представленный на фотографии телефон из прошлого имеет кнопку "SOS" на задней панели. Кнопка работает, даже когда раскладушка сложена.
В данном случае для звонка требуется сначала поднять телефонную трубку. В идеале, если бы она работала автономно, без связки с телефонным аппаратом, то, конечно, имело бы смысл делать на размыкание.
Спасибо за подсказку. Я даже нашёл б/у раскладушку с кнопками быстрого набора номера (М1, М2, М3). Б/у - потому что новых таких уже не делают. Но тактильно кнопки не выделяются, поэтому при плохом зрении не сильно помогают.
Даже в своей статье Вы показываете результат не на реальном устройстве.
Нет-нет, все скриншоты программ получены на реальном телефоне одновременным нажатием кнопки питания и уменьшения громкости. Эмулятор вообще не использовался нигде.
У зайчика, да, каюсь, подставил готовую цветастую картинку, сам не стал изобретать.
Ну, за исключением одной маленькой детали: под капотом свой Linux со своими репозитариями, из которых можно хоть средства разработки поставить. Живёшь с маленьким линуксом в кармане. К слову сказать, приложения андроида тоже можно запускать, но через эмулятор.
Мне кажется, жизнеспособным могло бы быть создание программных продуктов в университетских средах.
Когда изучение, например, виртуальной памяти иллюстрируется кодом своей же операционной системы, а план исполнения SQL-запроса описан в учебных пособиях.
Где курсовые проекты — написание или оптимизация существующего кода.
Где каждый новый студенческий курс модернизирует продукт. Весь код на виду, разработчики ровняются на стандарты, создаваемые в ведущих ВУЗах.
В книжных магазинах — курсы по операционным системам, СУБД, сетевым протоколам ссылаются на университетские образцы с открытыми исходными кодами.
Похоже на утопию, но примеры были. Например, в далёком 1970 — Berkley Ingres.
Однако, когда я последний раз смотрел на то, что они сделали, программа была сыроватой:
— не было поддержки русской (кириллической) клавиатуры
— кнопки были маленькими, при нестабильном определении направления взгляда
пользоваться такой клавиатурой было крайне сложно:
Интересно сравнить с моей программой образца 2014 года: клавиши гигантские, есть режим увеличения куска клавиатуры, если трудно попасть взглядом в кнопку («клавиатура в 2 шага»), ну и ряд других тонкостей, которые появились, как ответ на замечания от реальных людей (например, режим «спать», когда парализованный человек устаёт от светящего на него экрана и хочет его сделать тёмным, но выключить его руками не может).
Забавно, что в справке к программе от Microsoft они написали фразу «Привет! Как дела?» (см. рисунок выше), прямо как писал я в давней статье, хотя русской клавиатуры у них нет.
Но ничто не стоит на месте, надеюсь, что доведут до ума.
Когда я начинал работу над этой программой, не было TobiiDynavox. Были отдельно Dynavox со своим EyeMax, и отдельно Tobii. А ещё были Sensomotoric Instruments, TheEyeTribe, LC Technologies, EyeTech, Gazepoint и другие. У всех было нечто для взаимодействия с компьютером. Идеи витали в воздухе, и все друг у друга их заимствовали. Так что вы найдёте много похожего в продуктах этих компаний, хоть некоторые из этих компаний уже не существуют. Из последних поглощений: TobiiDynavox поглощает Smartbox — производителя софта для рисования озвучиваемых таблиц на экране, вроде той, что я здесь описал, но с функционалом покруче.
Насколько мне известно, в современных трекерах светодиоды синхронизированы с кадровой развёрткой камеры: один кадр подсвечивается левым светодиодом, второй кадр — правым. Так проще разделять блики на зрачках. Плюс, обработка картинки (поиск зрачков, бликов на зрачках, вычисление углов, под которыми свет ИК-светодиодов падает на зрачки с учётом кривизны зрачка, измеренной при калибровке, отсечение бликов от очков и контактных линз) может осуществляться как в самом айтрекере (встроенным компьютером), так и быть возложено на компьютер, к которому он подключён. В этом трекере большая часть обработки происходит внутри, и компьютер сильно не нагружается.
Ага, нашёл тут у себя недособранный компьютер...
Кавалеристы: — Вот это память у старика! (с)
Зря Вы так. Мне за код не стыдно. Там всего строк двести, но там есть и многопоточность, и перехват событий через SetWindowsHookEx. Код лаконичен, как и статья, но не обфусцирован, комментарии на месте. Привожу код ниже (убрал десяток закомментированных строк из того, что на гитхабе лежит).
Скрытый текст
К сожалению, Алиса не разбирает тихий голос и речь после инсульта. А так - да, это мне сразу посоветовали. Если бы к ней можно было бы кнопку подключить на такой запрос - цены бы ей не было. Возможно, я просто не знаю, а возможность такая есть.
Понимаю Ваши возражения. Ждал, что мне скажут, что надо было всё делать на микроконтроллере. Но реальность такова, что старый нетбук валялся без дела, а написание программы заняло несколько часов. Time-to-market пересилило остальные аргументы.
Я имел в виду не просто раскладушку, а раскладушку с кнопками быстрого набора. Кроме того, представленный на фотографии телефон из прошлого имеет кнопку "SOS" на задней панели. Кнопка работает, даже когда раскладушка сложена.
В данном случае для звонка требуется сначала поднять телефонную трубку. В идеале, если бы она работала автономно, без связки с телефонным аппаратом, то, конечно, имело бы смысл делать на размыкание.
Спасибо за подсказку. Я даже нашёл б/у раскладушку с кнопками быстрого набора номера (М1, М2, М3). Б/у - потому что новых таких уже не делают. Но тактильно кнопки не выделяются, поэтому при плохом зрении не сильно помогают.
Благодарю, послушать - вполне достаточно.
Спасибо, интересно. Где можно скачать финальную (Suno) версию песни "Дубовый дом"?
Нет-нет, все скриншоты программ получены на реальном телефоне одновременным нажатием кнопки питания и уменьшения громкости. Эмулятор вообще не использовался нигде.
У зайчика, да, каюсь, подставил готовую цветастую картинку, сам не стал изобретать.
Ну, за исключением одной маленькой детали: под капотом свой Linux со своими репозитариями, из которых можно хоть средства разработки поставить. Живёшь с маленьким линуксом в кармане.
К слову сказать, приложения андроида тоже можно запускать, но через эмулятор.
del (не туда)
Когда изучение, например, виртуальной памяти иллюстрируется кодом своей же операционной системы, а план исполнения SQL-запроса описан в учебных пособиях.
Где курсовые проекты — написание или оптимизация существующего кода.
Где каждый новый студенческий курс модернизирует продукт. Весь код на виду, разработчики ровняются на стандарты, создаваемые в ведущих ВУЗах.
В книжных магазинах — курсы по операционным системам, СУБД, сетевым протоколам ссылаются на университетские образцы с открытыми исходными кодами.
Похоже на утопию, но примеры были. Например, в далёком 1970 — Berkley Ingres.
Однако, когда я последний раз смотрел на то, что они сделали, программа была сыроватой:
— не было поддержки русской (кириллической) клавиатуры
— кнопки были маленькими, при нестабильном определении направления взгляда
пользоваться такой клавиатурой было крайне сложно:
Интересно сравнить с моей программой образца 2014 года: клавиши гигантские, есть режим увеличения куска клавиатуры, если трудно попасть взглядом в кнопку («клавиатура в 2 шага»), ну и ряд других тонкостей, которые появились, как ответ на замечания от реальных людей (например, режим «спать», когда парализованный человек устаёт от светящего на него экрана и хочет его сделать тёмным, но выключить его руками не может).
Забавно, что в справке к программе от Microsoft они написали фразу «Привет! Как дела?» (см. рисунок выше), прямо как писал я в давней статье, хотя русской клавиатуры у них нет.
Но ничто не стоит на месте, надеюсь, что доведут до ума.