コード例 #1
0
/**
 * \brief Set up calibration
 *
 * Allocates and initializes the application context; sets up the font, touch
 * event handler and calibration data; updates the display and then schedules
 * the calibration task.
 *
 * \param completed_task Task to schedule when calibration is complete
 */
void app_touch_calibrate_setup(struct workqueue_task *completed_task)
{
	calibrate_context =
			membag_alloc(sizeof(struct touch_calibrate_context));
	assert(calibrate_context != NULL);

	// Use twice as large font for this application.
	memcpy(&sysfont2x, &sysfont, sizeof(sysfont));
	sysfont2x.scale = 2;

	// Temporarily replace touch event handler.
	calibrate_context->old_handler = touch_get_event_handler();
	touch_set_event_handler(touch_calibrate_event_handler);

	// Clear the screen and draw the calibration guide text.
	gfx_set_clipping(0, 0, gfx_get_width(), gfx_get_height());
	gfx_draw_filled_rect(0, 0, gfx_get_width(), gfx_get_height(),
			CAL_BG_COLOR);
	gfx_draw_progmem_string((const char __progmem_arg *)
			&calibrate_help_text, 10, 80, &sysfont2x, CAL_FG_COLOR,
			GFX_COLOR_TRANSPARENT);

	// Set panel coordinates for all calibration points.
	calibrate_context->cal_points[0].panel_x =
			(gfx_get_width() - CAL_OFFSET - 1);
	calibrate_context->cal_points[0].panel_y =
			(gfx_get_height() - CAL_OFFSET - 1);
	calibrate_context->cal_points[1].panel_x = (CAL_OFFSET);
	calibrate_context->cal_points[1].panel_y =
			(gfx_get_height() - CAL_OFFSET - 1);
	calibrate_context->cal_points[2].panel_x = (CAL_OFFSET);
	calibrate_context->cal_points[2].panel_y = (CAL_OFFSET);

	// Draw circle for first calibration point.
	gfx_draw_circle(calibrate_context->cal_points[0].panel_x,
			calibrate_context->cal_points[0].panel_y,
			CAL_RADIUS, CAL_FG_COLOR, GFX_WHOLE);

	// Initialize the calibration state and tasks before scheduling it.
	calibrate_context->state = 0;
	calibrate_context->completed_task = completed_task;

	workqueue_task_init(&calibrate_context->task,
			touch_calibrate_task_handler);
	workqueue_add_task(&main_workqueue, &calibrate_context->task);
}
コード例 #2
0
/**
 * \brief Application task worker
 *
 * Waits for the touch release events generated after the user has touched a
 * calibration circle drawn on screen, then stores the calibration data and
 * draws the circle for the next calibration point.
 *
 * Three such calibration points are stored before the calibration matrix for
 * the touch driver is computed and assigned to it.
 *
 * \sa touch_driver_group
 *
 * \note If the raw samples of a calibration point do not differ by at least
 * \ref CAL_TRESHOLD from the previous calibration point, it is interpreted as
 * an unintended touch and ignored.
 *
 * \param task Workqueue task for this worker function
 */
static void touch_calibrate_task_handler(struct workqueue_task *task)
{
	int16_t                         dx;
	int16_t                         dy;
	struct touch_calibrate_context  *cal_ctx;

	cal_ctx = container_of(task, struct touch_calibrate_context, task);

	switch (cal_ctx->state) {
	case 0:	/* Fall through */
	case 1:
	case 2:
		// Schedule task to run once more
		workqueue_add_task(&main_workqueue, &cal_ctx->task);

		// Run until touch is released
		if (cal_ctx->event.type != TOUCH_RELEASE)
			break;

		// Store calibration values
		cal_ctx->event.type = TOUCH_NO_EVENT;
		cal_ctx->cal_points[cal_ctx->state].raw_x =
				cal_ctx->event.point.raw_x;
		cal_ctx->cal_points[cal_ctx->state].raw_y =
				cal_ctx->event.point.raw_y;

		dx = cal_ctx->cal_points[cal_ctx->state - 1].raw_x -
				cal_ctx->cal_points[cal_ctx->state].raw_x;
		dy = cal_ctx->cal_points[cal_ctx->state - 1].raw_y -
				cal_ctx->cal_points[cal_ctx->state].raw_y;
		dx = abs(dx);
		dy = abs(dy);

		// If point is too close to the last one, wait for another touch
		if ((dx < CAL_TRESHOLD) && (dy < CAL_TRESHOLD))
			break;

		// Clear old circle before moving on.
		gfx_draw_circle(cal_ctx->cal_points[cal_ctx->state].panel_x,
				cal_ctx->cal_points[cal_ctx->state].panel_y,
				CAL_RADIUS, CAL_BG_COLOR, GFX_WHOLE);

		// Move to next point
		cal_ctx->state++;

		/* Skip drawing last circles if we're done. */
		if (cal_ctx->state >= 3)
			break;

		// Draw next circle.
		gfx_draw_circle(cal_ctx->cal_points[cal_ctx->state].panel_x,
				cal_ctx->cal_points[cal_ctx->state].panel_y,
				CAL_RADIUS, CAL_FG_COLOR, GFX_WHOLE);
		break;

	case 3:
		// Calibration completed

		// Clear circle before moving on.
		gfx_draw_circle(cal_ctx->cal_points[2].panel_x,
				cal_ctx->cal_points[2].panel_y,
				CAL_RADIUS, CAL_BG_COLOR, GFX_WHOLE);

		// Compute and assign the calibration matrix to the driver.
		touch_compute_calibration_matrix(cal_ctx->cal_points,
				&cal_ctx->cal_matrix);

		touch_set_calibration_matrix(&cal_ctx->cal_matrix);

		// Restore handler
		touch_set_event_handler(cal_ctx->old_handler);

		// Can now free memory
		membag_free(calibrate_context);

		// Schedule task if available
		if (calibrate_context->completed_task) {
			workqueue_add_task(&main_workqueue,
					calibrate_context->completed_task);
		}

		break;
	}
}
コード例 #3
0
/**
 * This function is the window event handler for radio button widgets.
 * It handles all events sent to the windows composing the widget.
 *
 * \param win Window receiving the event.
 * \param type The event type.
 * \param data Custom data, depending on event type.
 *
 * \return True if the event was recognized and accepted.
 */
static bool wtk_radio_button_handler(struct win_window *win,
		enum win_event_type type, void const *data)
{
	struct win_command_event command;

	/* Custom data for windows of a widget points back to the widget itself. */
	struct wtk_radio_button *radio_button
		= (struct wtk_radio_button *)win_get_custom_data(win);

	switch (type) {
	case WIN_EVENT_DRAW:
	{
		/* For DRAW events, the data parameter points to the
		 * clipping region.
		 */
		struct win_clip_region const *clip
			= (struct win_clip_region const *)data;

		/* There should not be other windows in this widget. */
		Assert(win == radio_button->container);

		/* Draw radio button circle. */
		gfx_draw_circle(clip->origin.x +
				WTK_RADIOBUTTON_BUTTON_X,
				clip->origin.y +
				WTK_RADIOBUTTON_BUTTON_Y,
				WTK_RADIOBUTTON_RADIUS,
				WTK_RADIOBUTTON_BUTTON_COLOR,
				GFX_WHOLE);

		/* Draw radio button filled circle background. */
		if (WTK_RADIOBUTTON_BACKGROUND_COLOR != GFX_COLOR_TRANSPARENT) {
			gfx_draw_filled_circle(clip->origin.x +
				WTK_RADIOBUTTON_BUTTON_X,
				clip->origin.y +
				WTK_RADIOBUTTON_BUTTON_Y,
				WTK_RADIOBUTTON_RADIUS - 1,
				WTK_RADIOBUTTON_BACKGROUND_COLOR,
				GFX_WHOLE);
		}	

		/* Draw radio button select marker if selected. */
		if (radio_button->group->selected == radio_button) {
			gfx_draw_filled_circle(clip->origin.x +
					WTK_RADIOBUTTON_BUTTON_X,
					clip->origin.y +
					WTK_RADIOBUTTON_BUTTON_Y,
					WTK_RADIOBUTTON_RADIUS - 2,
					WTK_RADIOBUTTON_SELECT_COLOR,
					GFX_WHOLE);
		}

		/* Draw caption. */
		gfx_draw_string(radio_button->caption,
				clip->origin.x +
				WTK_RADIOBUTTON_CAPTION_X,
				clip->origin.y +
				WTK_RADIOBUTTON_CAPTION_Y, &sysfont,
				GFX_COLOR_TRANSPARENT,
				WTK_RADIOBUTTON_CAPTION_COLOR);

		/* Always accept DRAW events, as the return value is
		 * ignored anyway for that event type.
		 */
		return true;
	}

	case WIN_EVENT_POINTER:
	{
		/* There should not be other windows in this widget. */
		Assert(win == radio_button->container);

		/* For POINTER events, the data parameter points to the
		 * pointer event information.
		 */
		struct win_pointer_event const *event
			= (struct win_pointer_event const *)data;

		switch (event->type) {
		case WIN_POINTER_PRESS:

			/* When radio button pressed, grab pointer and
			 * wait for release inside widget borders.
			 * Other widgets won't get pointer events
			 * before it is released, and the pointer
			 * ungrabbed by us.
			 */
			if (radio_button->state ==
					WTK_RADIOBUTTON_NORMAL) {
				win_grab_pointer(radio_button->
						container);
				radio_button->state
					= WTK_RADIOBUTTON_PRESSED;
				win_redraw(radio_button->container);
			}

			break;

		case WIN_POINTER_RELEASE:

			/* When button released, take action only if
			 * released inside widget extents.
			 */
			if (radio_button->state ==
					WTK_RADIOBUTTON_PRESSED) {
				bool is_inside;

				/* Ungrab pointer. */
				win_grab_pointer(NULL);
				radio_button->state
					= WTK_RADIOBUTTON_NORMAL;
				win_redraw(radio_button->container);

				/* Check release position. */
				is_inside = win_is_inside_window
							(radio_button->
							container,
							&(event->pos));

				/* Select this radio button if inside. */
				if (is_inside) {
					wtk_radio_button_select
						(radio_button);

					/* Send non-zero command. */
					if (radio_button->command) {
						command.sender
							= radio_button->
								container;
						command.recipient
							= radio_button->
								container;
						command.data
							= radio_button->command;
						win_queue_command_event
							(&command);
					}
				}
			}

			break;

		default:
			break;
		}

		/* Accept all POINTER events since all acitivity inside
		 * the widget extents is relevant to us.
		 */
		return true;
	}

	case WIN_EVENT_DESTROY:
		/* There should not be other windows in this widget. */
		Assert(win == radio_button->container);

		/* Memory allocated for windows will be automatically destroyed
		 * by the window system. We must destroy other allocations.
		 */
		membag_free(radio_button->caption);

		/* Destroy radio group as well if we are the last one in the
		 * group. If not, remove ourselves from the group.
		 */
		--(radio_button->group->num_references);
		if (!radio_button->group->num_references) {
			membag_free(radio_button->group);
		} else {
			if (radio_button->group->selected == radio_button) {
				radio_button->group->selected = NULL;
			}
		}

		membag_free(radio_button);

		/* Always accept DESTROY events, as the return value is ignored
		 * anyway for that event type.
		 */
		return true;

	default:
		/* Reject unknown event types. */
		return false;
	}
}