前言:

阅读下面正文时,请时刻把握符号 -> ,因为作者将其作为整个分析的流程使用。

触摸逻辑分析:

SofTool.CN Notes:
获取数据,通常有两种方式:中断和查询。下面是作者主要通过中断的方式进行的分析过程。

通过中断方式获取触摸屏数据:

触笔触摸屏幕 ->

产生中断 ->

中断处理获得触摸物理坐标 ->

SofTool.CN Notes:
当然这个物理坐标也不是直接从AD出来的,而是需要你自己通过AD和修正值换算出来的。

注:
如果想通过查询方式获取数据,那么可以在程序中建立一个任务,来不断地调用 GUI_TOUCH_Exec() ,这里是需要调用四次的,每次读取一次 x 或 y 。
GUI_TOUCH_Exec() 函数位于 那么可以查看 core\GUI_TOUCH_DriverAnalog.c 。
该函数第一次读取调用 _StoreUnstable(x, y) 函数。这个函数中包含一个消除抖动的过程,处理的也很巧妙;
该函数第二次读取已经去掉抖动了,调用 GUI_TOUCH_StoreUnstable();

其后(仍在中断处理函数中)的处理过程:

GUI_TOUCH_Exec()

->

_StoreUnstable()

->

GUI_TOUCH_StoreUnstable(x, y)

->

GUI_TOUCH_StoreState(x, y)

在此函数中,改写局部静态变量 static GUI_PID_STATE _State 如下:

  if ((x >= 0) && (y >= 0)) {
    _State.Pressed = 1;
    _State.x = x;
    _State.y = y;
  } else {
    _State.Pressed = 0;
  }

->

GUI_TOUCH_StoreStateEx(&_State),做如下事:

  if (memcmp(pState, &_State, sizeof(_State))) {
    _State = *pState;

    GUI_PID_StoreState(pState);
  }

->

void GUI_PID_StoreState(const GUI_PID_STATE *pState) {
  _PID_Load();

  if (memcmp(&_State, pState, sizeof(_State))) {
    _State = *pState;

    GUI_X_SIGNAL_EVENT();
  }
}

->

static void _PID_Load(void) {
  #if (GUI_WINSUPPORT)
    WM_pfHandlePID = WM_HandlePID;
  #endif
}

至此 中断返回。

注:
GUI_Exec()函数可以通过GUI_WaitKey()来调用。

处理触摸屏数据

通过上面获取屏幕数据之后,下面就要开始处理该屏幕坐标数据了。

GUI_Exec()

进入以下函数,循环执行:

GUI_Exec() -> GUI_Exec1() -> WM_Exec()

->

WM_Exec1()

注:
该函数位于 WM.c ,在 WM_Exec1() 函数中做如下判断:

  if (WM_pfHandlePID) {
    if (WM_pfHandlePID())    //因此实际调用的函数是 WM_HandlePID ;
      return 1;               /* We have done something ... */
  }

->

WM_HandlePID()

分析函数 WM_HandlePID:(位于 WM\WMTouch.c 中)
1)GUI_PID_GetState(&StateNew); //获得刚刚触摸的状态
2)CHWin.hWin = _Screen2Win(&StateNew); //判断被触摸的是哪个东东!具体如下:

static WM_HWIN _Screen2Win(GUI_PID_STATE* pState) {
  if (WM__hCapture == 0) {
    return WM_Screen2hWin(pState->x, pState->y);
  }
  return WM__hCapture;
}

->

WM_HWIN WM_Screen2hWin(int x, int y) {
  WM_HWIN r;
  WM_LOCK();
  r = _Screen2hWin(WM__FirstWin, 0, x, y);//yhm20061211AC, WM__FirstWin = hNew; in //func _AddToLinList
  WM_UNLOCK();
  return r;        //yhm20061211AC,return the handle of the obj which is hitted!!!
}

->

static WM_HWIN _Screen2hWin(WM_HWIN hWin, WM_HWIN hStop, int x, int y) {
  WM_Obj* pWin = WM_HANDLE2PTR(hWin);
  WM_HWIN hChild;
  WM_HWIN hHit;
  /* First check if the  coordinates are in the given window. If not, return 0 */
  if (WM__IsInWindow(pWin, x, y) == 0) {
    return 0;
  }
  /* If the coordinates are in a child, search deeper ... */
  for (hChild = pWin->hFirstChild; hChild && (hChild != hStop); ) {
    WM_Obj* pChild = WM_HANDLE2PTR(hChild);
    if ((hHit = _Screen2hWin(hChild, hStop, x, y)) != 0) {
      hWin = hHit;        /* Found a window */
    }

    hChild = pChild->hNext;
  }

  return hWin;            /* No Child affected ... The parent is the right one */
}

->

WM__IsInModalArea(CHWin.hWin)

注:
判断是否模态?
如果是模态,则再看状态是否改变?if ((WM_PID__StateLast.Pressed != StateNew.Pressed) && CHWin.hWin)
如果是:

Msg.MsgId  = WM_PID_STATE_CHANGED;
WM__SendMessageIfEnabled(CHWin.hWin, &Msg);

举例:
以按钮为例,这时按钮的默认回调函数:BUTTON_Callback 就会收到这个 WM__SendMessageIfEnabled() 发来的消息,并调用WIDGET_HandleActive(hObj, pMsg),在此函数中有如下代码:

  case WM_PID_STATE_CHANGED:
    if (pWidget->State & WIDGET_STATE_FOCUSSABLE) {
      const WM_PID_STATE_CHANGED_INFO * pInfo = (const WM_PID_STATE_CHANGED_INFO*)pMsg->Data.p;
      if (pInfo->State) {                   //如果是按下,则设置焦点
        WM_SetFocus(hObj);
      }
}

让我们再返回到 WM_HandlePID() ,接下来处理触摸事件:Msg.MsgId = WM_TOUCH ; 其中包括 按下和提起 两个触摸事件,首先判断上次触摸的窗体和这次是否是同一个,如果不是,则先发送 WM_TOUCH 消息给上次触摸的窗体:

        if (StateNew.Pressed) {
              /* Moved out -> no longer in this window
               * Send a NULL pointer as data
               */
             Msg.Data.p = NULL;//留意这里
              //Uart_Printf("Move out/n");
            }
            WM__SendTouchMessage(WM__CHWinLast.hWin, &Msg);
            WM__CHWinLast.hWin = 0;

在 WM__SendTouchMessage() 函数中,还会把这个消息发送给上次触摸的窗体的所有父窗体呢!你看:

WM__SendPIDMessage() 位于 WMTouch.c

void WM__SendPIDMessage(WM_HWIN hWin, WM_MESSAGE* pMsg) {
  WM_MESSAGE Msg;

  /* Send message to the affected window */
  Msg = *pMsg;                 /* Save message as it may be modified in callback (as return value) */
  WM__SendMessageIfEnabled(hWin, &Msg);

  /* Send notification to all ancestors.
     We need to check if the window which has received the last message still exists,
     since it may have deleted itself and its parent as result of the message.
  */
  Msg.hWinSrc = hWin;
  Msg.MsgId   = WM_TOUCH_CHILD;
  while (WM_IsWindow(hWin)) {
    hWin = WM_GetParent(hWin);
    if (hWin) {
      Msg.Data.p  = pMsg;            /* Needs to be set for each window, as callback is allowed to modify it */
      WM__SendMessageIfEnabled(hWin, &Msg);    /* Send message to the ancestors */
    }
  }
}

假设上次触摸的控件是一个按钮,则这时 BUTTON_Callback() 就会收到这个消息:

BUTTON_Callback() 位于 Widget\BUTTON.c

  case WM_TOUCH:
_OnTouch(hObj, pObj, pMsg);

_OnTouch()函数如下:

对于按钮控件,则 _OnTouch() 位于 > BUTTON_Callback() 位于 Widget\BUTTON.c

static void _OnTouch(BUTTON_Handle hObj, BUTTON_Obj* pObj, WM_MESSAGE*pMsg) {
  const GUI_PID_STATE* pState = (const GUI_PID_STATE*)pMsg->Data.p;
#if BUTTON_REACT_ON_LEVEL
  if (!pMsg->Data.p) {  /* Mouse moved out */
    _ButtonReleased(hObj, pObj, WM_NOTIFICATION_MOVED_OUT);
  }
#else
  if (pMsg->Data.p) {  /* Something happened in our area (pressed or released) */
    if (pState->Pressed) {
      if ((pObj->Widget.State & BUTTON_STATE_PRESSED) == 0){  
       _ButtonPressed(hObj, pObj);
      }
    } else {
      /* React only if button was pressed before ... avoid problems with moving / hiding windows above (such as dropdown) */
      if (pObj->Widget.State & BUTTON_STATE_PRESSED) {  
        _ButtonReleased(hObj, pObj, WM_NOTIFICATION_RELEASED);
      }
    }
  } else {//在这个函数中将执行这个调用,因为pMsg->Data.p == NULL
    _ButtonReleased(hObj, pObj, WM_NOTIFICATION_MOVED_OUT);
  }
#endif

其中 _ButtonReleased() 函数如下:

static void _ButtonReleased(BUTTON_Handle hObj, BUTTON_Obj* pObj, int Notification) {
  WIDGET_AndState(hObj, BUTTON_STATE_PRESSED);
  if (pObj->Widget.Win.Status & WM_SF_ISVIS) {
    WM_NotifyParent(hObj, Notification);
  }

  if (Notification == WM_NOTIFICATION_RELEASED) {
    GUI_DEBUG_LOG("BUTTON: Hit/n");
    GUI_StoreKey(pObj->Widget.Id);
  }
}

由于调用了 WM_NotifyParent(),因此按钮的父窗体就会收到 WM_NOTIFICATION_MOVED_OUT 这个消息,我们假设其父窗体是个 FRAMEWIN_Obj,则 FRAMEWIN_Obj 的回调函数 FRAMEWIN__cbClient 以及用户自定义回调函数会处理这个消息。为什么会是这样,因为对一个对话框,其结构如下:

typedef struct {
  int MsgId;            /* type of message */
  WM_HWIN hWin;         /* Destination window */
  WM_HWIN hWinSrc;      /* Source window  */
  union {
    const void* p;            /* Some messages need more info ... Pointer is declared "const" because some systems (M16C) have 4 byte const, byte 2 byte default ptrs */
    int v;
    GUI_COLOR Color;
  } Data;
} WM_MESSAGE;

注:

  • 上面关于其结构的源码待考究?
  • FRAMEWIN__cbClient() 位于 Widget\FRAMEWIN.c

再次返回到 WM_HandlePID() ,接下来向本次触摸的窗体发送 WM_TOUCH 消息。
首先,如果是按下,则保存窗体句柄 WM__CHWinLast.hWin = CHWin.hWin ;然后发送消息:

Msg.Data.p = (void*)&StateNew;
WM__SendTouchMessage (CHWin.hWin, &Msg);

按照上面的分析,一直调用,再次进入 BUTTON.C 中的 _OnTouch() 函数:
此时,pMsg->Data.p != NULL,因此执行如下操作:

  if (pMsg->Data.p) {  /* Something happened in our area (pressed or released) */
    if (pState->Pressed) {
      if ((pObj->Widget.State & BUTTON_STATE_PRESSED) == 0){   //如果不是按下状态
       _ButtonPressed(hObj, pObj);
      }
    } else {
      /* React only if button was pressed before ... avoid problems with moving / hiding windows above (such as dropdown) */
      if (pObj->Widget.State & BUTTON_STATE_PRESSED) {   //如果是按下状态
        _ButtonReleased(hObj, pObj, WM_NOTIFICATION_RELEASED);
     }
  }

->

_ButtonReleased(hObj, pObj, WM_NOTIFICATION_RELEASED);
这个函数中,调用了:

void GUI_StoreKey(int Key) {
  if (!_Key) {
    _Key = Key;
  }
  GUI_X_SIGNAL_EVENT();
}

而在 GUI_WaitKey() 中会调用:

int GUI_GetKey(void) {
  int r = _Key;
  _Key = 0;
  return r;
}

static void _ButtonPressed (BUTTON_Handle hObj, BUTTON_Obj* pObj) {
  WIDGET_OrState(hObj, BUTTON_STATE_PRESSED);
  if (pObj->Widget.Win.Status & WM_SF_ISVIS) {
    WM_NotifyParent(hObj, WM_NOTIFICATION_CLICKED);// 产生单击事件
  }
}

此时,我们就可以在应用程序自定义的回调函数中处理按钮的按下和提起事件了。例如:

    case WM_NOTIFY_PARENT:
      Id    = WM_GetId(pMsg->hWinSrc);    /* Id of widget */
      NCode = pMsg->Data.v;               /* Notification code */
      switch (NCode) {
        case WM_NOTIFICATION_RELEASED:    /* React only if released */
          if (Id == GUI_ID_OK) {          /* OK Button */
                GUI_MessageBox("This text is shown/nin a message box",
                                   "Caption/Title", 
                                GUI_MESSAGEBOX_CF_MOVEABLE);
          }
          if (Id == GUI_ID_CANCEL) {      /* Cancel Button */
            GUI_EndDialog(hWin, 1);
          }
          break;
      }
      break;

作者:love33521
来源:https://blog.csdn.net/love33521/article/details/11961381