From 742a2235357bd8357dd35158a526e74ff58a69f7 Mon Sep 17 00:00:00 2001 From: "arcanfe@nospam" Date: Mon, 11 Apr 2016 17:59:44 +0000 Subject: [PATCH] (build) updated mouse support script from durden FossilOrigin-Name: e3b07b86058d9f33bedfda36bc5d170b37b95ff27e6d3a07c3ba59361027b142 --- data/resources/scripts/mouse.lua | 692 +++++++++++++++++++++++++++---- 1 file changed, 604 insertions(+), 88 deletions(-) diff --git a/data/resources/scripts/mouse.lua b/data/resources/scripts/mouse.lua index 97428d87d..f035e5f53 100644 --- a/data/resources/scripts/mouse.lua +++ b/data/resources/scripts/mouse.lua @@ -1,8 +1,59 @@ -- --- Copyright 2014, Björn Ståhl +-- Copyright 2014-2015, Björn Ståhl -- License: 3-Clause BSD. -- Reference: http://arcan-fe.com -- +-- all functions are prefixed with mouse_ +-- +-- setup (takes control of vid): +-- setup_native(vid, hs_x, hs_y) or +-- setup(vid, layer, pickdepth, cachepick, hidden) +-- layer : order value +-- pickdepth : number of stacked vids to check (typically 1) +-- cachepick : cache results to reduce picking calls +-- hidden : start in hidden state +-- +-- input: +-- button_input(ind, active) +-- input(x, y, state, mask_all_events) +-- absinput(x, y) +-- +-- output: +-- mouse_xy() +-- +-- tuning: +-- autohide() - true or false ; call to flip state, returns new state +-- acceleration(x_scale_factor, y_scale_factor) ; +-- dblclickrate(rate or nil) opt:rate ; get or set rate +-- +-- state change: +-- hide, show +-- add_cursor(label, vid, hs_dx, hs_dy) +-- constrain(min_x, min_y, max_x, max_y) +-- switch_cursor(label) ; switch active cursor to label +-- +-- overlay selection: +-- select_begin(vid, handler, constrain{x1,y1,x2,y2}) +-- select_end +-- +-- use: +-- addlistener(tbl, {event1, event2, ...}) +-- possible events: drag, drop, click, over, out +-- dblclick, rclick, press, release, motion +-- +-- tbl- fields: +-- own (function, req, callback(tbl, vid) +-- return true/false for ownership of vid +-- +-- + matching functions for set of events +-- +-- droplistener(tbl) +-- tick(steps) +-- + input (above) +-- +-- debug: +-- increase debuglevel > 2 and outputs activity +-- -- -- Mouse- gesture / collision triggers @@ -13,6 +64,7 @@ local mouse_handlers = { out = {}, drag = {}, press = {}, + button = {}, release = {}, drop = {}, hover = {}, @@ -36,34 +88,77 @@ local mstate = { -- mouse event is triggered accel_x = 1, accel_y = 1, - dblclickstep = 12, -- maximum number of ticks between clicks for dblclick - drag_delta = 8, -- wiggle-room for drag + dblclickstep = 12, -- maximum number of ticks between clicks for dblclick + drag_delta = 4, -- wiggle-room for drag hover_ticks = 30, -- time of inactive cursor before hover is triggered hover_thresh = 12, -- pixels movement before hover is released - click_timeout= 8; -- maximum number of ticks before a press-release pair isn't a tick + click_timeout= 14; -- maximum number of ticks before we won't emit click click_cnt = 0, counter = 0, hover_count = 0, x_ofs = 0, y_ofs = 0, last_hover = 0, + dev = 0, x = 0, - y = 0 + y = 0, + min_x = 0, + min_y = 0, + max_x = VRESW, + max_y = VRESH, + hotspot_x = 0, + hotspot_y = 0, + scale_w = 1, -- scale factors for cursor drawing + scale_h = 1, }; -local function mouse_cursorupd(x, y) - if (mstate.hidden and (x ~= 0 or y ~= 0)) then +local cursors = { +}; - if (mstate.native == nil) then - instant_image_transform(mstate.cursor); - blend_image(mstate.cursor, 1.0, 10); - mstate.hidden = false; +local function lock_constrain() +-- locking to surface is slightly odd in that we still need to return +-- valid relative motion which may or may not come from a relative source +-- and still handle constraints e.g. warp/clamp + if (mstate.lockvid) then + local props = image_surface_resolve_properties(mstate.lockvid); + local ul_x = props.x; + local ul_y = props.y; + local lr_x = props.x + props.width; + local lr_y = props.y + props.height; + + if (mstate.warp) then + local cpx = math.floor(props.x + 0.5 * props.width); + local cpy = math.floor(props.y + 0.5 * props.height); + input_samplebase(mstate.dev, cpx, cpy); + mstate.x = cpx; + mstate.y = cpy; + else + mstate.x = mstate.x < ul_x and ul_x or mstate.x; + mstate.y = mstate.y < ul_y and ul_y or mstate.y; + mstate.x = mstate.x > lr_x and lr_x or mstate.x; + mstate.y = mstate.y > lr_y and lr_y or mstate.y; end - elseif (mstate.hidden) then - return 0, 0; +-- when we always get absolute coordinates even with relative motion, +-- we need to track the spill and offset.. + local nx = mstate.x; + local ny = mstate.y; + mstate.rel_x = (mstate.rel_x + mstate.x) < ul_x + and (mstate.x - ul_x) or mstate.rel_x; + mstate.rel_x = mstate.rel_x + mstate.x > lr_x + and lr_x - mstate.x or mstate.rel_x; + mstate.rel_y = mstate.rel_y + mstate.y < ul_y + and mstate.y - ul_y or mstate.rel_y; + mstate.rel_y = mstate.rel_y + mstate.y > lr_y + and lr_y - mstate.y or mstate.rel_y; + +-- resolve properties is expensive so return the values in the hope that they +-- might be re-usable by some other part + return ul_x, ul_y, lr_x, lr_y; end +end +local function mouse_cursorupd(x, y) x = x * mstate.accel_x; y = y * mstate.accel_y; @@ -75,35 +170,56 @@ local function mouse_cursorupd(x, y) mstate.x = mstate.x < 0 and 0 or mstate.x; mstate.y = mstate.y < 0 and 0 or mstate.y; - mstate.x = mstate.x > VRESW and VRESW-1 or mstate.x; - mstate.y = mstate.y > VRESH and VRESH-1 or mstate.y; + mstate.x = mstate.x > mstate.max_x and mstate.max_x - 1 or mstate.x; + mstate.y = mstate.y > mstate.max_y and mstate.max_y - 1 or mstate.y; mstate.hide_count = mstate.hide_base; + local relx = mstate.x - lmx; + local rely = mstate.y - lmy; + + lock_constrain(); if (mstate.native) then move_cursor(mstate.x, mstate.y); else move_image(mstate.cursor, mstate.x + mstate.x_ofs, mstate.y + mstate.y_ofs); end - return (mstate.x - lmx), (mstate.y - lmy); + + return relx, rely; end -- global event handlers for things like switching cursor on press mstate.lmb_global_press = function() - mstate.y_ofs = 2; - mstate.x_ofs = 2; - mouse_cursorupd(0, 0); - end + mstate.y_ofs = 2; + mstate.x_ofs = 2; + mouse_cursorupd(0, 0); +end mstate.lmb_global_release = function() - mstate.x_ofs = 0; - mstate.y_ofs = 0; - mouse_cursorupd(0, 0); - end + mstate.x_ofs = 0; + mstate.y_ofs = 0; + mouse_cursorupd(0, 0); +end -- this can be overridden to cache previous queries mouse_pickfun = pick_items; +local function def_reveal() + for i=1,20 do + local surf = color_surface(16, 16, 0, 255, 0); + show_image(surf); + local seed = math.random(80) - 40; + order_image(surf, 65534); + local intime = math.random(50); + local mx, my = mouse_xy(); + move_image(surf, mx, my); + nudge_image(surf, math.random(150) - 75, + math.random(150) - 75, intime); + expire_image(surf, intime); + blend_image(surf, 0.0, intime); + end +end + local function linear_find(table, label) for a,b in pairs(table) do if (b == label) then return a end @@ -147,10 +263,33 @@ local function linear_find_vid(table, vid, state) end end -local function cached_pick(xpos, ypos, depth, nitems) +local function select_regupd() + local x, y = mouse_xy(); + local x2 = mstate.in_select.x; + local y2 = mstate.in_select.y; + + if (x > x2) then + local tx = x; + x = x2; + x2 = tx; + end + + if (y > y2) then + local ty = y; + y = y2; + y2 = ty; + end + + return x, y, x2, y2; +end + +-- re-use cached results if the cursor hasn't moved and the logical clock +-- hasn't changed since last query, this cuts down on I/O storms from high- +-- res devices +local function cached_pick(xpos, ypos, depth, reverse) if (mouse_lastpick == nil or CLOCK > mouse_lastpick.tick or xpos ~= mouse_lastpick.x or ypos ~= mouse_lastpick.y) then - local res = pick_items(xpos, ypos, depth, nitems); + local res = pick_items(xpos, ypos, depth, reverse, mstate.rt); mouse_lastpick = { tick = CLOCK, @@ -174,6 +313,10 @@ function mouse_state() return mstate; end +function mouse_toxy(x, y, time) +-- disable all event handlers and visibly move cursor to position +end + function mouse_destroy() mouse_handlers = {}; mouse_handlers.click = {}; @@ -196,7 +339,7 @@ function mouse_destroy() mstate.accel_x = 1; mstate.accel_y = 1; mstate.dblclickstep = 6; - mstate.drag_delta = 8; + mstate.drag_delta = 4; mstate.hover_ticks = 30; mstate.hover_thresh = 12; mstate.counter = 0; @@ -205,35 +348,39 @@ function mouse_destroy() mstate.y_ofs = 0; mstate.last_hover = 0; toggle_mouse_grab(MOUSE_GRABOFF); + + for k,v in pairs(cursors) do + delete_image(v.vid); + end + cursors = {}; + if (valid_vid(mstate.cursor)) then delete_image(mstate.cursor); mstate.cursor = BADID; end end --- --- Load / Prepare cursor, read default acceleration and --- filtering settings. --- cvid : video id of image to use as cursor (will take control of id) --- clayer : which ordervalue for cursor to have --- pickdepth : how many vids beneath cvid should be concidered? --- cachepick : avoid unecessary --- hidden : start in hidden state or not --- function mouse_setup(cvid, clayer, pickdepth, cachepick, hidden) - mstate.cursor = cvid; mstate.hidden = false; - mstate.x = math.floor(VRESW * 0.5); - mstate.y = math.floor(VRESH * 0.5); + mstate.x = math.floor(mstate.max_x * 0.5); + mstate.y = math.floor(mstate.max_y * 0.5); - if (hidden ~= nil and hidden ~= true) then - else - show_image(cvid); + mstate.cursor = null_surface(1, 1); + image_mask_set(mstate.cursor, MASK_UNPICKABLE); + mouse_add_cursor("default", cvid, 0, 0); + mstate.rt = rt; + mouse_switch_cursor(); + + if (not hidden) then + show_image(mstate.cursor); end - move_image(cvid, mstate.x, mstate.y); + move_image(mstate.cursor, mstate.x, mstate.y); + local props = image_surface_properties(cvid); + mstate.size = {props.width, props.height}; + mstate.pickdepth = pickdepth; - order_image(cvid, clayer); + order_image(mstate.cursor, clayer); image_mask_set(cvid, MASK_UNPICKABLE); if (cachepick) then mouse_pickfun = cached_pick; @@ -244,22 +391,29 @@ function mouse_setup(cvid, clayer, pickdepth, cachepick, hidden) mouse_cursorupd(0, 0); end -function mouse_setup_native(resimg) +function mouse_setup_native(resimg, hs_x, hs_y) + mstate.native = true; + if (hs_x == nil) then + hs_x = 0; + hs_y = 0; + end + +-- wash out any other dangling properties in resimg local tmp = null_surface(1, 1); local props = image_surface_properties(resimg); - image_sharestorage(resimg, tmp); - cursor_setstorage(resimg); delete_image(resimg); - mstate.cursor_vid = tmp; - mstate.native = true; - mstate.x = math.floor(VRESW * 0.5); - mstate.y = math.floor(VRESH * 0.5); + mouse_add_cursor("default", tmp, hs_x, hs_y); + mouse_switch_cursor(); + + mstate.x = math.floor(mstate.max_x * 0.5); + mstate.y = math.floor(mstate.max_y * 0.5); mstate.pickdepth = 1; mouse_pickfun = cached_pick; resize_cursor(props.width, props.height); + mstate.size = {props.width, props.height}; mouse_cursorupd(0, 0); end @@ -268,13 +422,20 @@ end -- Some devices just give absolute movements, convert -- these to relative before moving on -- -function mouse_absinput(x, y) +function mouse_absinput(x, y, nofwd) + local rx = x - mstate.x; + local ry = y - mstate.y; + local arx = mstate.accel_x * rx; + local ary = mstate.accel_y * ry; + + mstate.rel_x = arx; + mstate.rel_y = ary; - mstate.rel_x = x - mstate.x; - mstate.rel_y = y - mstate.y; + mstate.x = x + (arx - rx); + mstate.y = y + (ary - ry); - mstate.x = x; - mstate.y = y; +-- also need to constrain the relative coordinates when we clamp + local ulx, uly, lrx, lry = lock_constrain(); if (mstate.native) then move_cursor(mstate.x, mstate.y); @@ -283,7 +444,32 @@ function mouse_absinput(x, y) mstate.y + mstate.y_ofs); end - mouse_input(x, y, nil, true); + if (not nofwd) then + mouse_input(mstate.x, mstate.y, nil, true); + end +end + +-- +-- ignore all mouse motion and possibly forward to specificed +-- funtion. will reset when valid_vid(vid) fails. +-- +function mouse_lockto(vid, fun, warp, state) + local olv = mstate.lockvid; + local olf = mstate.lockfun; + + if (valid_vid(vid)) then + mstate.lockvid = vid; + mstate.lockfun = fun; + mstate.lockstate = state; + mstate.warp = warp ~= nil and warp or false; + else + mstate.lockvid = nil; + mstate.lockfun = nil; + mstate.lockstate = nil; + mstate.warp = false; + end + + return olv, olf; end function mouse_xy() @@ -301,12 +487,16 @@ local function mouse_drag(x, y) warning(string.format("mouse_drag(%d, %d)", x, y)); end + local hitc = 0; for key, val in pairs(mstate.drag.list) do local res = linear_find_vid(mstate.handlers.drag, val, "drag"); if (res) then res:drag(val, x, y); + hitc = hitc + 1; end end + + return hitc; end local function rmbhandler(hists, press) @@ -350,7 +540,7 @@ local function lmbhandler(hists, press) mstate.lmb_global_release(); for key, val in pairs(hists) do - local res = linear_find_vid(mstate.handlers.press, val, "release"); + local res = linear_find_vid(mstate.handlers.release, val, "release"); if (res) then if (res:release(val, mstate.x, mstate.y)) then break; @@ -405,9 +595,29 @@ local function lmbhandler(hists, press) end end - mstate.counter = 0; - mstate.predrag = nil; - mstate.drag = nil; + mstate.counter = 0; + mstate.predrag = nil; + mstate.drag = nil; + end +end + +local function mouse_lockh(relx, rely) + local x, y = mouse_xy(); +-- safeguard from deletions that don't clean up after themselves + if (not valid_vid(mstate.lockvid)) then + mouse_lockto(); + elseif (mstate.lockfun) then + mstate.lockfun(relx, rely, x, y, mstate.lockstate); + end +end + +local function mouse_btnlock(ind, active) + local x, y = mouse_xy(); + if (not valid_vid(mstate.lockvid)) then + mouse_lockto(nil, nil); + if (mstate.lockfun) then + mstate.lockfun(x, y, 0, 0, ind, active); + end end end @@ -416,12 +626,38 @@ end -- button update at once for backwards compatibility. -- function mouse_button_input(ind, active) - if (ind < 1 or ind > 3) then + if (ind < 1 or ind > #mstate.btns) then return; end - local hists = mouse_pickfun(mstate.x, mstate.y, mstate.pickdepth, 1); + if (mstate.lockvid) then + return mouse_btnlock(ind, active); + end + + local hists = mouse_pickfun( + mstate.x + mstate.hotspot_x, + mstate.y + mstate.hotspot_y, mstate.pickdepth, 1, mstate.rt); + + if (DEBUGLEVEL > 2) then + local res = {} + print("button matches:"); + for i, v in ipairs(hists) do + print("\t" .. tostring(v) .. ":" .. (image_tracetag(v) ~= nil + and image_tracetag(v) or "unknown")); + end + print("\n"); + end + if (#mstate.handlers.button > 0) then + for key, val in pairs(hists) do + local res = linear_find_vid(mstate.handlers.button, val, "button"); + if (res) then + res:button(val, ind, active, mstate.x, mstate.y); + end + end + end + + mstate.in_handler = true; if (ind == 1 and active ~= mstate.btns[1]) then lmbhandler(hists, active); end @@ -430,6 +666,7 @@ function mouse_button_input(ind, active) rmbhandler(hists, active); end + mstate.in_handler = false; mstate.btns[ind] = active; end @@ -448,7 +685,33 @@ local function mbh(hists, state) mstate.btns[3] = state[3]; end +function mouse_reveal_hook(state) + if (type(state) == "function") then + mstate.reveal_hook = state; + elseif (state) then + mstate.reveal_hook = def_reveal; + else + mstate.reveal_hook = nil; + end +end + function mouse_input(x, y, state, noinp) + if (not mstate.revmask and mstate.hidden and (x ~= 0 or y ~= 0)) then + + if (mstate.native == nil) then + instant_image_transform(mstate.cursor); + blend_image(mstate.cursor, 1.0, 10); + mstate.hidden = false; + end + + if (mstate.reveal_hook) then + mstate.reveal_hook(); + end + + elseif (mstate.hidden) then + return 0, 0; + end + if (noinp == nil or noinp == false) then x, y = mouse_cursorupd(x, y); else @@ -456,9 +719,14 @@ function mouse_input(x, y, state, noinp) y = mstate.rel_y; end + if (mstate.lockvid or mstate.lockfun) then + return mouse_lockh(x, y); + end + + mstate.in_handler = true; mstate.hover_count = 0; - if (#mstate.hover_track > 0) then + if (not mstate.hover_ign and #mstate.hover_track > 0) then local dx = math.abs(mstate.hover_x - mstate.x); local dy = math.abs(mstate.hover_y - mstate.y); @@ -481,24 +749,18 @@ function mouse_input(x, y, state, noinp) local hists = mouse_pickfun(mstate.x, mstate.y, mstate.pickdepth, 1); if (mstate.drag) then - mouse_drag(x, y); + local hitc = mouse_drag(x, y); if (state ~= nil) then mbh(hists, state); end - return; - end - - for i=1,#hists do - if (linear_find(mstate.cur_over, hists[i]) == nil) then - table.insert(mstate.cur_over, hists[i]); - local res = linear_find_vid(mstate.handlers.over, hists[i], "over"); - if (res) then - res:over(hists[i], mstate.x, mstate.y); - end + mstate.in_handler = false; + if (hitc > 0) then + return; end end --- drop ones no longer selected +-- drop ones no longer selected, do out before over as many handlers will +-- do something that is overwritten by the following over event for i=#mstate.cur_over,1,-1 do if (not linear_ifind(hists, mstate.cur_over[i])) then local res = linear_find_vid(mstate.handlers.out,mstate.cur_over[i],"out"); @@ -515,21 +777,36 @@ function mouse_input(x, y, state, noinp) end end + for i=1,#hists do + if (linear_find(mstate.cur_over, hists[i]) == nil) then + table.insert(mstate.cur_over, hists[i]); + local res = linear_find_vid(mstate.handlers.over, hists[i], "over"); + if (res) then + res:over(hists[i], mstate.x, mstate.y); + end + end + end + if (mstate.predrag) then - mstate.predrag.count = mstate.predrag.count - - (math.abs(x) + math.abs(y)); + x, y = mouse_xy(); + local dx = math.abs(mstate.press_x - x); + local dy = math.abs(mstate.press_y - y); + local dist = math.sqrt(dx * dx + dy * dy); - if (mstate.predrag.count <= 0) then + if (dist >= mstate.predrag.count) then mstate.drag = mstate.predrag; mstate.predrag = nil; + mouse_drag(x - mstate.press_x, y - mstate.press_y); end end if (state == nil) then + mstate.in_handler = false; return; end mbh(hists, state); + mstate.in_handler = false; end mouse_motion = mouse_input; @@ -567,6 +844,14 @@ function mouse_addlistener(tbl, events) end end +function mouse_handlercount() + local cnt = 0; + for ind, val in pairs(mstate.handlers) do + cnt = cnt + #val; + end + return cnt; +end + function mouse_dumphandlers() warning("mouse_dumphandlers()"); @@ -581,9 +866,6 @@ function mouse_dumphandlers() warning("/mouse_dumphandlers()"); end --- --- Removes tbl from all callback tables --- function mouse_droplistener(tbl) for key, val in pairs( mstate.handlers ) do for ind, vtbl in ipairs( val ) do @@ -595,18 +877,123 @@ function mouse_droplistener(tbl) end end +function mouse_add_cursor(label, img, hs_x, hs_y) + if (label == nil or type(label) ~= "string") then + if (valid_vid(img)) then + delete_image(img); + end + return warning("mouse_add_cursor(), missing label or wrong type"); + end + + if (cursors[label] ~= nil) then + delete_image(cursors[label].vid); + end + + if (not valid_vid(img)) then + return warning(string.format( + "mouse_add_cursor(%s), missing image", label)); + end + + local props = image_storage_properties(img); + cursors[label] = { + vid = img, + hotspot_x = hs_x, + hotspot_y = hs_y, + width = props.width, + height = props.height + }; +end + +function mouse_scale(factor) + if (not mouse.prescale) then + mouse.prescale = { + }; + end + cursor.accel_x = mouse.prescale.ax * factor; + cursor.accel_y = mouse.prescale.ay * factor; +end + +function mouse_cursor_sf(fx, fy) + mstate.scale_w = fx and fx or 1.0; + mstate.scale_h = fy and fy or 1.0; + local new_w = mstate.size[1] * mstate.scale_w; + local new_h = mstate.size[2] * mstate.scale_h; + + if (mstate.native) then + resize_cursor(new_w, new_h); + else + resize_image(mstate.cursor, new_w, new_h); + end +end + +-- assumed: .vid(valid), .hotspot_x, .hotspot_y +function mouse_custom_cursor(ct) + if (mstate.native) then + cursor_setstorage(ct.vid); + else + image_sharestorage(ct.vid, mstate.cursor); + end + mstate.size = {ct.width, ct.height}; + mouse_cursor_sf(mstate.scale_w, mstate.scale_h); + +-- hotspot isn't scaled yet + local hsdx = mstate.hotspot_x - ct.hotspot_x; + local hsdy = mstate.hotspot_y - ct.hotspot_y; + +-- offset current position (as that might've triggered the event that +-- incited the caller to switch cursor by the change in label + + mstate.x = mstate.x + hsdx; + mstate.y = mstate.y + hsdy; + mstate.hotspot_x = ct.hotspot_x; + mstate.hotspot_y = ct.hotspot_y; +end + +function mouse_switch_cursor(label) + if (label == nil) then + label = "default"; + end + + if (label == mstate.active_label) then + return; + end + + mstate.active_label = label; + + if (cursors[label] == nil) then + if (mstate.native) then + cursor_setstorage(WORLDID); + else + hide_image(mstate.cursor); + end + return; + end + + local ct = cursors[label]; + mouse_custom_cursor(ct); +end + function mouse_hide() if (mstate.native) then - cursor_setstorage(WORLDID); + mouse_switch_cursor(nil); else instant_image_transform(mstate.cursor); blend_image(mstate.cursor, 0.0, 20, INTERP_EXPOUT); end end +function mouse_autohide() + mstate.autohide = not mstate.autohide; + return mstate.autohide; +end + +function mouse_hidemask(st) + mstate.revmask = st; +end + function mouse_show() if (mstate.native) then - cursor_setstorage(mstate.cursor_vid); + mouse_switch_cursor(mstate.active_label); else instant_image_transform(mstate.cursor); blend_image(mstate.cursor, 1.0, 20, INTERP_EXPOUT); @@ -618,20 +1005,19 @@ function mouse_tick(val) mstate.hover_count = mstate.hover_count + 1; mstate.click_cnt = mstate.click_cnt > 0 and mstate.click_cnt - 1 or 0; - if (mstate.autohide and mstate.hidden == false) then + if (not mstate.drag and mstate.autohide and mstate.hidden == false) then mstate.hide_count = mstate.hide_count - val; if (mstate.hide_count <= 0 and mstate.native == nil) then mstate.hidden = true; instant_image_transform(mstate.cursor); mstate.hide_count = mstate.hide_base; blend_image(mstate.cursor, 0.0, 20, INTERP_EXPOUT); + return; end end + mstate.in_handler = true; local hval = mstate.hover_ticks; - if (CLOCK - mstate.last_hover < 200) then - hval = 2; - end if (mstate.hover_count > hval) then if (hover_reset) then @@ -654,6 +1040,7 @@ function mouse_tick(val) else hover_reset = true; end + mstate.in_handler = false; end function mouse_dblclickrate(newr) @@ -664,6 +1051,135 @@ function mouse_dblclickrate(newr) end end +function mouse_querytarget(rt) + if (rt == nil) then + rt = WORLDID; + end + + local props = image_surface_properties(rt); + if (mstate.native) then + else + rendertarget_attach(rt, mstate.cursor, RENDERTARGET_DETACH); + end + mstate.max_x = props.width; + mstate.max_y = props.height; + if (mstate.rt ~= rt) then + mstate.rt = rt; + mouse_select_end(); + end +end + +-- vid will be the object that will be promoted to cursor order +-- and used to indicate the selected region. it's drawing setup +-- should match the desired behavior (so for pattern region rather +-- than blended, one needs repeating etc.) +function mouse_select_begin(vid, constrain) + if (not valid_vid(vid)) then + return false; + end + + if (mstate.in_select) then + mouse_select_end(); + end + + mstate.in_select = { + x = mstate.x, + y = mstate.y, + vid = vid, + hidden = mstate.hidden, + autodelete = {}, + lockvid = mstate.lockvid, + lockfun = mstate.lockfun + }; + + mouse_show(); +-- just save any old constrain- vid and create a new one that match + if (constrain) then + assert(constrain[4] - constrain[2] > 0); + assert(constrain[3] - constrain[1] > 0); + assert(constrain[1] >= 0 and constrain[2] >= 0); + assert(constrain[3] <= mstate.max_x and constrain[4] <= mstate.max_y); + local newlock = null_surface( + constrain[3] - constrain[1], + constrain[4] - constrain[2]); + mstate.lockvid = newlock; + move_image(newlock, constrain[1], constrain[2]); + table.insert(mstate.in_select.autodelete, lockvid); + end + +-- set order + link_image(vid, mstate.cursor); + image_mask_clear(vid, MASK_POSITION); + image_mask_set(vid, MASK_UNPICKABLE); + image_inherit_order(vid, true); + order_image(vid, -1); + resize_image(vid, 1, 1); + move_image(vid, mstate.x, mstate.y); + table.insert(mstate.in_select.autodelete, vid); + +-- normal constrain- handler will deal with clamping etc. + mstate.lockvid = null_surface(MAX_SURFACEW, MAX_SURFACEH); + mstate.lockfun = function() + local x1, y1, x2, y2 = select_regupd(); + local w = x2 - x1; + local h = y2 - y1; + if (w <= 0 or h <= 0) then + return; + end + move_image(vid, x1, y1); + resize_image(vid, w, h); + end + + return true; +end + +function mouse_select_set(vid) + if (not mstate.lockfun or not mstate.in_select) then + return; + end + + if (valid_vid(vid)) then + local props = image_surface_resolve_properties(vid); + mstate.x = props.x + props.width; + mstate.y = props.y + props.height; + mstate.in_select.x = props.x; + mstate.in_select.y = props.y; + mouse_cursorupd(0, 0); + mstate.lockfun(); + else + local mx, my = mouse_xy(); + mstate.in_select.x = mx; + mstate.in_select.y = my; + mouse_cursorupd(0, 0); + mstate.lockfun(); + end +end + +-- explicit end, handler will return region +function mouse_select_end(handler) + if (not mstate.in_select) then + return; + end + + if (mstate.hidden) then + mouse_hide(); + end + + delete_image(mstate.lockvid); + mstate.lockfun = mstate.in_select.lockfun; + mstate.lockvid = mstate.in_select.lockvid; + + for i,v in ipairs(mstate.in_select.autodelete) do + delete_image(v); + end + + if (handler) then + handler(select_regupd()); + end + + mstate.in_select = nil; +end + function mouse_acceleration(newvx, newvy) if (newvx == nil) then return mstate.accel_x, mstate.accel_y;