Commit 04022490 authored by Sergey Radionov's avatar Sergey Radionov Committed by Jean-Baptiste Kempf

common/win32_fullscreen: Attaching to libvlc events moved to VLCWindowsManager

Signed-off-by: Jean-Baptiste Kempf's avatarJean-Baptiste Kempf <jb@videolan.org>
parent 4d5793c0
...@@ -94,6 +94,11 @@ VLCHolderWnd* VLCHolderWnd::CreateHolderWindow(HWND hParentWnd, VLCWindowsManage ...@@ -94,6 +94,11 @@ VLCHolderWnd* VLCHolderWnd::CreateHolderWindow(HWND hParentWnd, VLCWindowsManage
return 0; return 0;
} }
libvlc_media_player_t* VLCHolderWnd::getMD() const
{
return _WindowsManager->getMD();
}
LRESULT CALLBACK VLCHolderWnd::VLCHolderClassWndProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam) LRESULT CALLBACK VLCHolderWnd::VLCHolderClassWndProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
{ {
VLCHolderWnd* h_data = reinterpret_cast<VLCHolderWnd*>(GetWindowLongPtr(hWnd, GWLP_USERDATA)); VLCHolderWnd* h_data = reinterpret_cast<VLCHolderWnd*>(GetWindowLongPtr(hWnd, GWLP_USERDATA));
...@@ -125,8 +130,6 @@ LRESULT CALLBACK VLCHolderWnd::VLCHolderClassWndProc(HWND hWnd, UINT uMsg, WPARA ...@@ -125,8 +130,6 @@ LRESULT CALLBACK VLCHolderWnd::VLCHolderClassWndProc(HWND hWnd, UINT uMsg, WPARA
h_data->_hMouseHook = h_data->_hMouseHook =
SetWindowsHookEx(WH_MOUSE, VLCHolderWnd::MouseHookProc, SetWindowsHookEx(WH_MOUSE, VLCHolderWnd::MouseHookProc,
NULL, GetWindowThreadProcessId(hChildWnd, NULL)); NULL, GetWindowThreadProcessId(hChildWnd, NULL));
//all work done, we don't need any notifications more
h_data->DetachFromLibVlcEventSystem();
} }
break; break;
} }
...@@ -181,55 +184,32 @@ LRESULT CALLBACK VLCHolderWnd::MouseHookProc(int nCode, WPARAM wParam, LPARAM lP ...@@ -181,55 +184,32 @@ LRESULT CALLBACK VLCHolderWnd::MouseHookProc(int nCode, WPARAM wParam, LPARAM lP
return 1; return 1;
} }
void VLCHolderWnd::OnLibVlcEvent(const libvlc_event_t* event, void *param) //libvlc events arrives from separate thread
void VLCHolderWnd::OnLibVlcEvent(const libvlc_event_t* event)
{ {
VLCHolderWnd* _this = reinterpret_cast<VLCHolderWnd*>(param); //We need set hook to catch doubleclicking (to switch to fullscreen and vice versa).
if(!_this->_hMouseHook){ //But libvlc media window may not exist yet,
//and we don't know when it will be created, nor ThreadId of it.
//So we try catch events,
//(suppose wnd will be ever created),
//and then try set mouse hook.
if(!_hMouseHook){
//libvlc events arrives from separate thread, //libvlc events arrives from separate thread,
//so we need post message to main thread, to notify it. //so we need post message to main thread, to notify it.
PostMessage(_this->getHWND(), WM_TRY_SET_MOUSE_HOOK, 0, 0); PostMessage(getHWND(), WM_TRY_SET_MOUSE_HOOK, 0, 0);
}
}
void VLCHolderWnd::LibVlcAttach(libvlc_media_player_t* p_md)
{
if(!_p_md){
_p_md = p_md;
libvlc_media_player_set_hwnd(p_md, getHWND());
libvlc_event_manager_t* em = libvlc_media_player_event_manager(_p_md);
if(em){
//We need set hook to catch doubleclicking (to switch to fullscreen and vice versa).
//But libvlc media window not exist yet,
//and we don't know when it will be created, nor ThreadId of it.
//So we try catch first libvlc_MediaPlayerPositionChanged event,
//(suppose wnd will be created to that moment)),
//and then try set mouse hook.
_LibVlcESAttached =
0==libvlc_event_attach(em, libvlc_MediaPlayerPositionChanged,
OnLibVlcEvent, this);
}
} }
} }
void VLCHolderWnd::DetachFromLibVlcEventSystem() void VLCHolderWnd::LibVlcAttach()
{ {
libvlc_event_manager_t* em = libvlc_media_player_event_manager(_p_md); libvlc_media_player_set_hwnd(getMD(), getHWND());
if(_LibVlcESAttached && em){
libvlc_event_detach(em, libvlc_MediaPlayerPositionChanged,
OnLibVlcEvent, this);
_LibVlcESAttached=false;
}
} }
void VLCHolderWnd::LibVlcDetach() void VLCHolderWnd::LibVlcDetach()
{ {
if(_p_md){ libvlc_media_player_t* p_md = getMD();
DetachFromLibVlcEventSystem(); if(p_md)
libvlc_media_player_set_hwnd(p_md, 0);
libvlc_media_player_set_hwnd(_p_md, 0);
_p_md=0;
}
if(_hMouseHook){ if(_hMouseHook){
UnhookWindowsHookEx(_hMouseHook); UnhookWindowsHookEx(_hMouseHook);
...@@ -243,7 +223,6 @@ HINSTANCE VLCFullScreenWnd::_hinstance = 0; ...@@ -243,7 +223,6 @@ HINSTANCE VLCFullScreenWnd::_hinstance = 0;
ATOM VLCFullScreenWnd::_fullscreen_wndclass_atom = 0; ATOM VLCFullScreenWnd::_fullscreen_wndclass_atom = 0;
ATOM VLCFullScreenWnd::_fullscreen_controls_wndclass_atom = 0; ATOM VLCFullScreenWnd::_fullscreen_controls_wndclass_atom = 0;
bool VLCFullScreenWnd::handle_position_changed_event_enabled = true;
void VLCFullScreenWnd::RegisterWndClassName(HINSTANCE hInstance) void VLCFullScreenWnd::RegisterWndClassName(HINSTANCE hInstance)
{ {
//save hInstance for future use //save hInstance for future use
...@@ -339,12 +318,8 @@ LRESULT CALLBACK VLCFullScreenWnd::FSWndWindowProc(HWND hWnd, UINT uMsg, WPARAM ...@@ -339,12 +318,8 @@ LRESULT CALLBACK VLCFullScreenWnd::FSWndWindowProc(HWND hWnd, UINT uMsg, WPARAM
break; break;
case WM_SHOWWINDOW:{ case WM_SHOWWINDOW:{
if(FALSE==wParam){ //hidding if(FALSE==wParam){ //hidding
fs_data->UnRegisterEvents();
break; break;
} }
else{//showing
fs_data->RegisterEvents();
}
fs_data->NeedShowControls(); fs_data->NeedShowControls();
...@@ -781,126 +756,42 @@ void VLCFullScreenWnd::CreateToolTip() ...@@ -781,126 +756,42 @@ void VLCFullScreenWnd::CreateToolTip()
///////////////////////////////////// /////////////////////////////////////
//VLCFullScreenWnd event handlers //VLCFullScreenWnd event handlers
void VLCFullScreenWnd::handle_position_changed_event(const libvlc_event_t* event, void *param) void VLCFullScreenWnd::handle_position_changed_event(const libvlc_event_t* event)
{ {
VLCFullScreenWnd* fs_data = reinterpret_cast<VLCFullScreenWnd *>(param); SyncVideoPosScrollPosWithVideoPos();
if(fs_data->handle_position_changed_event_enabled)
fs_data->SyncVideoPosScrollPosWithVideoPos();
} }
void VLCFullScreenWnd::handle_input_state_event(const libvlc_event_t* event, void *param) void VLCFullScreenWnd::handle_input_state_event(const libvlc_event_t* event)
{ {
VLCFullScreenWnd* fs_data = reinterpret_cast<VLCFullScreenWnd *>(param);
switch( event->type ) switch( event->type )
{ {
case libvlc_MediaPlayerNothingSpecial:
break;
case libvlc_MediaPlayerOpening:
break;
case libvlc_MediaPlayerBuffering:
break;
case libvlc_MediaPlayerPlaying: case libvlc_MediaPlayerPlaying:
SendMessage(fs_data->hPlayPauseButton, BM_SETIMAGE, (WPARAM)IMAGE_BITMAP, (LPARAM)fs_data->hPauseBitmap); SendMessage(hPlayPauseButton, BM_SETIMAGE, (WPARAM)IMAGE_BITMAP, (LPARAM)hPauseBitmap);
break; break;
case libvlc_MediaPlayerPaused: case libvlc_MediaPlayerPaused:
SendMessage(fs_data->hPlayPauseButton, BM_SETIMAGE, (WPARAM)IMAGE_BITMAP, (LPARAM)fs_data->hPlayBitmap); SendMessage(hPlayPauseButton, BM_SETIMAGE, (WPARAM)IMAGE_BITMAP, (LPARAM)hPlayBitmap);
break; break;
case libvlc_MediaPlayerStopped: case libvlc_MediaPlayerStopped:
SendMessage(fs_data->hPlayPauseButton, BM_SETIMAGE, (WPARAM)IMAGE_BITMAP, (LPARAM)fs_data->hPlayBitmap); SendMessage(hPlayPauseButton, BM_SETIMAGE, (WPARAM)IMAGE_BITMAP, (LPARAM)hPlayBitmap);
break;
case libvlc_MediaPlayerForward:
break;
case libvlc_MediaPlayerBackward:
break;
case libvlc_MediaPlayerEndReached:
break;
case libvlc_MediaPlayerEncounteredError:
break; break;
} }
} }
void VLCFullScreenWnd::RegisterEvents() //libvlc events arrives from separate thread
void VLCFullScreenWnd::OnLibVlcEvent(const libvlc_event_t* event)
{ {
libvlc_media_player_t* p_md = getMD(); if( !_WindowsManager->IsFullScreen() )
if( p_md ){ return;
libvlc_event_manager_t *eventManager = NULL;
eventManager = libvlc_media_player_event_manager(p_md);
if(eventManager) {
// libvlc_event_attach(eventManager, libvlc_MediaPlayerNothingSpecial,
// VLCFullScreenWnd::handle_input_state_event, this);
// libvlc_event_attach(eventManager, libvlc_MediaPlayerOpening,
// VLCFullScreenWnd::handle_input_state_event, this);
// libvlc_event_attach(eventManager, libvlc_MediaPlayerBuffering,
// VLCFullScreenWnd::handle_input_state_event, this);
libvlc_event_attach(eventManager, libvlc_MediaPlayerPlaying,
VLCFullScreenWnd::handle_input_state_event, this);
libvlc_event_attach(eventManager, libvlc_MediaPlayerPaused,
VLCFullScreenWnd::handle_input_state_event, this);
libvlc_event_attach(eventManager, libvlc_MediaPlayerStopped,
VLCFullScreenWnd::handle_input_state_event, this);
// libvlc_event_attach(eventManager, libvlc_MediaPlayerForward,
// VLCFullScreenWnd::handle_input_state_event, this);
// libvlc_event_attach(eventManager, libvlc_MediaPlayerBackward,
// VLCFullScreenWnd::handle_input_state_event, this);
// libvlc_event_attach(eventManager, libvlc_MediaPlayerEndReached,
// VLCFullScreenWnd::handle_input_state_event, this);
// libvlc_event_attach(eventManager, libvlc_MediaPlayerEncounteredError,
// VLCFullScreenWnd::handle_input_state_event, this);
// libvlc_event_attach(eventManager, libvlc_MediaPlayerTimeChanged,
// VLCFullScreenWnd::handle_time_changed_event, this);
libvlc_event_attach(eventManager, libvlc_MediaPlayerPositionChanged,
VLCFullScreenWnd::handle_position_changed_event, this);
// libvlc_event_attach(eventManager, libvlc_MediaPlayerSeekableChanged,
// handle_seekable_changed_event, this);
// libvlc_event_attach(eventManager, libvlc_MediaPlayerPausableChanged,
// handle_pausable_changed_event, this);
}
}
}
void VLCFullScreenWnd::UnRegisterEvents() switch(event->type){
{ case libvlc_MediaPlayerPlaying:
libvlc_media_player_t* p_md = getMD(); case libvlc_MediaPlayerPaused:
if( p_md ){ case libvlc_MediaPlayerStopped:
libvlc_event_manager_t *eventManager = NULL; handle_input_state_event(event);
break;
eventManager = libvlc_media_player_event_manager(p_md); case libvlc_MediaPlayerPositionChanged:
if(eventManager) { handle_position_changed_event(event);
// libvlc_event_detach(eventManager, libvlc_MediaPlayerNothingSpecial, break;
// VLCFullScreenWnd::handle_input_state_event, this);
// libvlc_event_detach(eventManager, libvlc_MediaPlayerOpening,
// VLCFullScreenWnd::handle_input_state_event, this);
// libvlc_event_detach(eventManager, libvlc_MediaPlayerBuffering,
// VLCFullScreenWnd::handle_input_state_event, this);
libvlc_event_detach(eventManager, libvlc_MediaPlayerPlaying,
handle_input_state_event, this);
libvlc_event_detach(eventManager, libvlc_MediaPlayerPaused,
handle_input_state_event, this);
libvlc_event_detach(eventManager, libvlc_MediaPlayerStopped,
handle_input_state_event, this);
// libvlc_event_detach(eventManager, libvlc_MediaPlayerForward,
// VLCFullScreenWnd::handle_input_state_event, this);
// libvlc_event_detach(eventManager, libvlc_MediaPlayerBackward,
// VLCFullScreenWnd::handle_input_state_event, this);
// libvlc_event_detach(eventManager, libvlc_MediaPlayerEndReached,
// VLCFullScreenWnd::handle_input_state_event, this);
// libvlc_event_detach(eventManager, libvlc_MediaPlayerEncounteredError,
// VLCFullScreenWnd::handle_input_state_event, this);
// libvlc_event_detach(eventManager, libvlc_MediaPlayerTimeChanged,
// VLCFullScreenWnd::handle_time_changed_event, this);
libvlc_event_detach(eventManager, libvlc_MediaPlayerPositionChanged,
VLCFullScreenWnd::handle_position_changed_event, this);
// libvlc_event_detach(eventManager, libvlc_MediaPlayerSeekableChanged,
// handle_seekable_changed_event, this);
// libvlc_event_detach(eventManager, libvlc_MediaPlayerPausableChanged,
// handle_pausable_changed_event, this);
}
} }
} }
...@@ -908,7 +799,7 @@ void VLCFullScreenWnd::UnRegisterEvents() ...@@ -908,7 +799,7 @@ void VLCFullScreenWnd::UnRegisterEvents()
//VLCWindowsManager //VLCWindowsManager
/////////////////////// ///////////////////////
VLCWindowsManager::VLCWindowsManager(HMODULE hModule) VLCWindowsManager::VLCWindowsManager(HMODULE hModule)
:_hModule(hModule), _hWindowedParentWnd(0), _HolderWnd(0), _FSWnd(0), :_hModule(hModule), _hWindowedParentWnd(0), _p_md(0), _HolderWnd(0), _FSWnd(0),
_b_new_messages_flag(false), Last_WM_MOUSEMOVE_Pos(0) _b_new_messages_flag(false), Last_WM_MOUSEMOVE_Pos(0)
{ {
VLCHolderWnd::RegisterWndClassName(hModule); VLCHolderWnd::RegisterWndClassName(hModule);
...@@ -952,13 +843,23 @@ void VLCWindowsManager::LibVlcAttach(libvlc_media_player_t* p_md) ...@@ -952,13 +843,23 @@ void VLCWindowsManager::LibVlcAttach(libvlc_media_player_t* p_md)
if(!_HolderWnd) if(!_HolderWnd)
return;//VLCWindowsManager::CreateWindows was not called return;//VLCWindowsManager::CreateWindows was not called
_HolderWnd->LibVlcAttach(p_md); if(!_p_md){
_p_md = p_md;
VlcEvents(true);
}
_HolderWnd->LibVlcAttach();
} }
void VLCWindowsManager::LibVlcDetach() void VLCWindowsManager::LibVlcDetach()
{ {
if(_HolderWnd) if(_HolderWnd)
_HolderWnd->LibVlcDetach(); _HolderWnd->LibVlcDetach();
if(_p_md){
VlcEvents(false);
_p_md = 0;
}
} }
void VLCWindowsManager::StartFullScreen() void VLCWindowsManager::StartFullScreen()
...@@ -1033,3 +934,57 @@ void VLCWindowsManager::OnMouseEvent(UINT uMouseMsg) ...@@ -1033,3 +934,57 @@ void VLCWindowsManager::OnMouseEvent(UINT uMouseMsg)
} }
} }
} }
//libvlc events arrives from separate thread
void VLCWindowsManager::OnLibVlcEvent_proxy(const libvlc_event_t* event, void *param)
{
VLCWindowsManager* WM = static_cast<VLCWindowsManager*>(param);
WM->OnLibVlcEvent(event);
}
void VLCWindowsManager::VlcEvents(bool Attach)
{
libvlc_media_player_t* p_md = getMD();
if( !p_md )
return;
libvlc_event_manager_t* em =
libvlc_media_player_event_manager(p_md);
if(!em)
return;
for(int e=libvlc_MediaPlayerMediaChanged; e<=libvlc_MediaPlayerVout; ++e){
switch(e){
//case libvlc_MediaPlayerMediaChanged:
//case libvlc_MediaPlayerNothingSpecial:
//case libvlc_MediaPlayerOpening:
//case libvlc_MediaPlayerBuffering:
case libvlc_MediaPlayerPlaying:
case libvlc_MediaPlayerPaused:
case libvlc_MediaPlayerStopped:
//case libvlc_MediaPlayerForward:
//case libvlc_MediaPlayerBackward:
//case libvlc_MediaPlayerEndReached:
//case libvlc_MediaPlayerEncounteredError:
//case libvlc_MediaPlayerTimeChanged:
case libvlc_MediaPlayerPositionChanged:
//case libvlc_MediaPlayerSeekableChanged:
//case libvlc_MediaPlayerPausableChanged:
//case libvlc_MediaPlayerTitleChanged:
//case libvlc_MediaPlayerSnapshotTaken:
//case libvlc_MediaPlayerLengthChanged:
//case libvlc_MediaPlayerVout:
if(Attach)
libvlc_event_attach(em, e, OnLibVlcEvent_proxy, this);
else
libvlc_event_detach(em, e, OnLibVlcEvent_proxy, this);
break;
}
}
}
void VLCWindowsManager::OnLibVlcEvent(const libvlc_event_t* event)
{
if(_HolderWnd) _HolderWnd->OnLibVlcEvent(event);
if(_FSWnd) _FSWnd->OnLibVlcEvent(event);
}
...@@ -38,19 +38,19 @@ public: ...@@ -38,19 +38,19 @@ public:
static VLCHolderWnd* CreateHolderWindow(HWND hParentWnd, VLCWindowsManager* WM); static VLCHolderWnd* CreateHolderWindow(HWND hParentWnd, VLCWindowsManager* WM);
void DestroyWindow(); void DestroyWindow();
libvlc_media_player_t* getMD() const {return _p_md;} void LibVlcAttach();
void LibVlcAttach(libvlc_media_player_t* p_md);
void LibVlcDetach(); void LibVlcDetach();
//libvlc events arrives from separate thread
void OnLibVlcEvent(const libvlc_event_t* event);
private: private:
static LPCTSTR getClassName(void) { return TEXT("VLC ActiveX Window Holder Class"); }; static LPCTSTR getClassName(void) { return TEXT("VLC ActiveX Window Holder Class"); };
static LRESULT CALLBACK VLCHolderClassWndProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam); static LRESULT CALLBACK VLCHolderClassWndProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam);
static LRESULT CALLBACK MouseHookProc(int nCode, WPARAM wParam, LPARAM lParam); static LRESULT CALLBACK MouseHookProc(int nCode, WPARAM wParam, LPARAM lParam);
HHOOK _hMouseHook; HHOOK _hMouseHook;
static void OnLibVlcEvent(const libvlc_event_t* event, void *param); libvlc_media_player_t* getMD() const;
void DetachFromLibVlcEventSystem();
private: private:
static HINSTANCE _hinstance; static HINSTANCE _hinstance;
...@@ -59,8 +59,7 @@ private: ...@@ -59,8 +59,7 @@ private:
private: private:
VLCHolderWnd(HWND hWnd, VLCWindowsManager* WM) VLCHolderWnd(HWND hWnd, VLCWindowsManager* WM)
: _hMouseHook(NULL), _hWnd(hWnd) : _hMouseHook(NULL), _hWnd(hWnd)
, _WindowsManager(WM), _p_md(0) , _WindowsManager(WM){};
, _LibVlcESAttached(false){};
public: public:
HWND getHWND() const {return _hWnd;} HWND getHWND() const {return _hWnd;}
...@@ -68,8 +67,6 @@ public: ...@@ -68,8 +67,6 @@ public:
private: private:
HWND _hWnd; HWND _hWnd;
VLCWindowsManager* _WindowsManager; VLCWindowsManager* _WindowsManager;
libvlc_media_player_t* _p_md;
bool _LibVlcESAttached;
}; };
/////////////////////// ///////////////////////
...@@ -120,9 +117,6 @@ private: ...@@ -120,9 +117,6 @@ private:
} }
private: private:
void RegisterEvents();
void UnRegisterEvents();
void SetVideoPosScrollRangeByVideoLen(); void SetVideoPosScrollRangeByVideoLen();
void SyncVideoPosScrollPosWithVideoPos(); void SyncVideoPosScrollPosWithVideoPos();
void SetVideoPos(float Pos); //0-start, 1-end void SetVideoPos(float Pos); //0-start, 1-end
...@@ -132,6 +126,8 @@ private: ...@@ -132,6 +126,8 @@ private:
public: public:
void NeedShowControls(); void NeedShowControls();
//libvlc events arrives from separate thread
void OnLibVlcEvent(const libvlc_event_t* event);
private: private:
void NeedHideControls(); void NeedHideControls();
...@@ -163,10 +159,8 @@ private: ...@@ -163,10 +159,8 @@ private:
private: private:
void SetVideoPosScrollPosByVideoPos(libvlc_time_t CurPos); void SetVideoPosScrollPosByVideoPos(libvlc_time_t CurPos);
static bool handle_position_changed_event_enabled; void handle_position_changed_event(const libvlc_event_t* event);
static void handle_position_changed_event(const libvlc_event_t* event, void *param); void handle_input_state_event(const libvlc_event_t* event);
//static void handle_time_changed_event(const libvlc_event_t* event, void *param);
static void handle_input_state_event(const libvlc_event_t* event, void *param);
public: public:
HWND getHWND() const {return _hWnd;} HWND getHWND() const {return _hWnd;}
...@@ -200,7 +194,7 @@ public: ...@@ -200,7 +194,7 @@ public:
HMODULE getHModule() const {return _hModule;}; HMODULE getHModule() const {return _hModule;};
VLCHolderWnd* getHolderWnd() const {return _HolderWnd;} VLCHolderWnd* getHolderWnd() const {return _HolderWnd;}
VLCFullScreenWnd* getFullScreenWnd() const {return _FSWnd;} VLCFullScreenWnd* getFullScreenWnd() const {return _FSWnd;}
libvlc_media_player_t* getMD() const {return _HolderWnd?_HolderWnd->getMD():0;} libvlc_media_player_t* getMD() const {return _p_md;}
public: public:
void setNewMessageFlag(bool Yes) void setNewMessageFlag(bool Yes)
...@@ -210,10 +204,18 @@ public: ...@@ -210,10 +204,18 @@ public:
public: public:
void OnMouseEvent(UINT uMouseMsg); void OnMouseEvent(UINT uMouseMsg);
private:
void VlcEvents(bool Attach);
//libvlc events arrives from separate thread
static void OnLibVlcEvent_proxy(const libvlc_event_t* event, void *param);
void OnLibVlcEvent(const libvlc_event_t* event);
private: private:
HMODULE _hModule; HMODULE _hModule;
HWND _hWindowedParentWnd; HWND _hWindowedParentWnd;
libvlc_media_player_t* _p_md;
VLCHolderWnd* _HolderWnd; VLCHolderWnd* _HolderWnd;
VLCFullScreenWnd* _FSWnd; VLCFullScreenWnd* _FSWnd;
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment