int SDL_UpperBlitScaled(SDL_Surface * src, const SDL_Rect * srcrect, SDL_Surface * dst, SDL_Rect * dstrect) { double src_x0, src_y0, src_x1, src_y1; double dst_x0, dst_y0, dst_x1, dst_y1; SDL_Rect final_src, final_dst; double scaling_w, scaling_h; int src_w, src_h; int dst_w, dst_h; /* Make sure the surfaces aren't locked */ if (!src || !dst) { return SDL_SetError("SDL_UpperBlitScaled: passed a NULL surface"); } if (src->locked || dst->locked) { return SDL_SetError("Surfaces must not be locked during blit"); } if (NULL == srcrect) { src_w = src->w; src_h = src->h; } else { src_w = srcrect->w; src_h = srcrect->h; } if (NULL == dstrect) { dst_w = dst->w; dst_h = dst->h; } else { dst_w = dstrect->w; dst_h = dstrect->h; } if (dst_w == src_w && dst_h == src_h) { /* No scaling, defer to regular blit */ return SDL_BlitSurface(src, srcrect, dst, dstrect); } scaling_w = (double)dst_w / src_w; scaling_h = (double)dst_h / src_h; if (NULL == dstrect) { dst_x0 = 0; dst_y0 = 0; dst_x1 = dst_w - 1; dst_y1 = dst_h - 1; } else { dst_x0 = dstrect->x; dst_y0 = dstrect->y; dst_x1 = dst_x0 + dst_w - 1; dst_y1 = dst_y0 + dst_h - 1; } if (NULL == srcrect) { src_x0 = 0; src_y0 = 0; src_x1 = src_w - 1; src_y1 = src_h - 1; } else { src_x0 = srcrect->x; src_y0 = srcrect->y; src_x1 = src_x0 + src_w - 1; src_y1 = src_y0 + src_h - 1; /* Clip source rectangle to the source surface */ if (src_x0 < 0) { dst_x0 -= src_x0 * scaling_w; src_x0 = 0; } if (src_x1 >= src->w) { dst_x1 -= (src_x1 - src->w + 1) * scaling_w; src_x1 = src->w - 1; } if (src_y0 < 0) { dst_y0 -= src_y0 * scaling_h; src_y0 = 0; } if (src_y1 >= src->h) { dst_y1 -= (src_y1 - src->h + 1) * scaling_h; src_y1 = src->h - 1; } } /* Clip destination rectangle to the clip rectangle */ /* Translate to clip space for easier calculations */ dst_x0 -= dst->clip_rect.x; dst_x1 -= dst->clip_rect.x; dst_y0 -= dst->clip_rect.y; dst_y1 -= dst->clip_rect.y; if (dst_x0 < 0) { src_x0 -= dst_x0 / scaling_w; dst_x0 = 0; } if (dst_x1 >= dst->clip_rect.w) { src_x1 -= (dst_x1 - dst->clip_rect.w + 1) / scaling_w; dst_x1 = dst->clip_rect.w - 1; } if (dst_y0 < 0) { src_y0 -= dst_y0 / scaling_h; dst_y0 = 0; } if (dst_y1 >= dst->clip_rect.h) { src_y1 -= (dst_y1 - dst->clip_rect.h + 1) / scaling_h; dst_y1 = dst->clip_rect.h - 1; } /* Translate back to surface coordinates */ dst_x0 += dst->clip_rect.x; dst_x1 += dst->clip_rect.x; dst_y0 += dst->clip_rect.y; dst_y1 += dst->clip_rect.y; final_src.x = (int)SDL_floor(src_x0 + 0.5); final_src.y = (int)SDL_floor(src_y0 + 0.5); final_src.w = (int)SDL_floor(src_x1 - src_x0 + 1.5); final_src.h = (int)SDL_floor(src_y1 - src_y0 + 1.5); final_dst.x = (int)SDL_floor(dst_x0 + 0.5); final_dst.y = (int)SDL_floor(dst_y0 + 0.5); final_dst.w = (int)SDL_floor(dst_x1 - dst_x0 + 1.5); final_dst.h = (int)SDL_floor(dst_y1 - dst_y0 + 1.5); if (final_dst.w < 0) final_dst.w = 0; if (final_dst.h < 0) final_dst.h = 0; if (dstrect) *dstrect = final_dst; if (final_dst.w == 0 || final_dst.h == 0 || final_src.w <= 0 || final_src.h <= 0) { /* No-op. */ return 0; } return SDL_LowerBlitScaled(src, &final_src, dst, &final_dst); }
/* Check to see if we need to synthesize focus events */ static SDL_bool SDL_UpdateMouseFocus(SDL_Window * window, int x, int y, Uint32 buttonstate) { SDL_Mouse *mouse = SDL_GetMouse(); SDL_bool inWindow = SDL_TRUE; if (window && ((window->flags & SDL_WINDOW_MOUSE_CAPTURE) == 0)) { int w, h; SDL_GetWindowSize(window, &w, &h); if (x < 0 || y < 0 || x >= w || y >= h) { inWindow = SDL_FALSE; } } /* Linux doesn't give you mouse events outside your window unless you grab the pointer. Windows doesn't give you mouse events outside your window unless you call SetCapture(). Both of these are slightly scary changes, so for now we'll punt and if the mouse leaves the window you'll lose mouse focus and reset button state. */ #ifdef SUPPORT_DRAG_OUTSIDE_WINDOW if (!inWindow && !buttonstate) { #else if (!inWindow) { #endif if (window == mouse->focus) { #ifdef DEBUG_MOUSE printf("Mouse left window, synthesizing move & focus lost event\n"); #endif SDL_PrivateSendMouseMotion(window, mouse->mouseID, 0, x, y); SDL_SetMouseFocus(NULL); } return SDL_FALSE; } if (window != mouse->focus) { #ifdef DEBUG_MOUSE printf("Mouse entered window, synthesizing focus gain & move event\n"); #endif SDL_SetMouseFocus(window); SDL_PrivateSendMouseMotion(window, mouse->mouseID, 0, x, y); } return SDL_TRUE; } int SDL_SendMouseMotion(SDL_Window * window, SDL_MouseID mouseID, int relative, int x, int y) { if (window && !relative) { SDL_Mouse *mouse = SDL_GetMouse(); if (!SDL_UpdateMouseFocus(window, x, y, mouse->buttonstate)) { return 0; } } return SDL_PrivateSendMouseMotion(window, mouseID, relative, x, y); } static int GetScaledMouseDelta(float scale, int value, float *accum) { if (scale != 1.0f) { *accum += scale * value; if (*accum >= 0.0f) { value = (int)SDL_floor(*accum); } else { value = (int)SDL_ceil(*accum); } *accum -= value; } return value; } static int SDL_PrivateSendMouseMotion(SDL_Window * window, SDL_MouseID mouseID, int relative, int x, int y) { SDL_Mouse *mouse = SDL_GetMouse(); int posted; int xrel; int yrel; if (mouseID == SDL_TOUCH_MOUSEID && !mouse->touch_mouse_events) { return 0; } if (mouseID != SDL_TOUCH_MOUSEID && mouse->relative_mode_warp) { int center_x = 0, center_y = 0; SDL_GetWindowSize(window, ¢er_x, ¢er_y); center_x /= 2; center_y /= 2; if (x == center_x && y == center_y) { mouse->last_x = center_x; mouse->last_y = center_y; return 0; } SDL_WarpMouseInWindow(window, center_x, center_y); } if (relative) { if (mouse->relative_mode) { x = GetScaledMouseDelta(mouse->relative_speed_scale, x, &mouse->scale_accum_x); y = GetScaledMouseDelta(mouse->relative_speed_scale, y, &mouse->scale_accum_y); } else { x = GetScaledMouseDelta(mouse->normal_speed_scale, x, &mouse->scale_accum_x); y = GetScaledMouseDelta(mouse->normal_speed_scale, y, &mouse->scale_accum_y); } xrel = x; yrel = y; x = (mouse->last_x + xrel); y = (mouse->last_y + yrel); } else { xrel = x - mouse->last_x; yrel = y - mouse->last_y; } /* Drop events that don't change state */ if (!xrel && !yrel) { #ifdef DEBUG_MOUSE printf("Mouse event didn't change state - dropped!\n"); #endif return 0; } /* Ignore relative motion when first positioning the mouse */ if (!mouse->has_position) { xrel = 0; yrel = 0; mouse->has_position = SDL_TRUE; } /* Ignore relative motion positioning the first touch */ if (mouseID == SDL_TOUCH_MOUSEID && !mouse->buttonstate) { xrel = 0; yrel = 0; } /* Update internal mouse coordinates */ if (!mouse->relative_mode) { mouse->x = x; mouse->y = y; } else { mouse->x += xrel; mouse->y += yrel; } /* make sure that the pointers find themselves inside the windows, unless we have the mouse captured. */ if (window && ((window->flags & SDL_WINDOW_MOUSE_CAPTURE) == 0)) { int x_max = 0, y_max = 0; /* !!! FIXME: shouldn't this be (window) instead of (mouse->focus)? */ SDL_GetWindowSize(mouse->focus, &x_max, &y_max); --x_max; --y_max; if (mouse->x > x_max) { mouse->x = x_max; } if (mouse->x < 0) { mouse->x = 0; } if (mouse->y > y_max) { mouse->y = y_max; } if (mouse->y < 0) { mouse->y = 0; } } mouse->xdelta += xrel; mouse->ydelta += yrel; /* Move the mouse cursor, if needed */ if (mouse->cursor_shown && !mouse->relative_mode && mouse->MoveCursor && mouse->cur_cursor) { mouse->MoveCursor(mouse->cur_cursor); } /* Post the event, if desired */ posted = 0; if (SDL_GetEventState(SDL_MOUSEMOTION) == SDL_ENABLE) { SDL_Event event; event.motion.type = SDL_MOUSEMOTION; event.motion.windowID = mouse->focus ? mouse->focus->id : 0; event.motion.which = mouseID; event.motion.state = mouse->buttonstate; event.motion.x = mouse->x; event.motion.y = mouse->y; event.motion.xrel = xrel; event.motion.yrel = yrel; posted = (SDL_PushEvent(&event) > 0); } if (relative) { mouse->last_x = mouse->x; mouse->last_y = mouse->y; } else { /* Use unclamped values if we're getting events outside the window */ mouse->last_x = x; mouse->last_y = y; } return posted; }