<commit_before>/**
 * The MIT License
 *
 * Copyright (C) 2017 Kiyofumi Kondoh
 *
 * Permission is hereby granted, free of charge, to any person obtaining a copy
 * of this software and associated documentation files (the "Software"), to deal
 * in the Software without restriction, including without limitation the rights
 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
 * copies of the Software, and to permit persons to whom the Software is
 * furnished to do so, subject to the following conditions:
 *
 * The above copyright notice and this permission notice shall be included in
 * all copies or substantial portions of the Software.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
 * THE SOFTWARE.
 */

#include "stdafx.h"
#include "test_cef_offscreen.h"

extern int main_win(HINSTANCE hInstance);
extern int main_win_term();

#include "include/base/cef_lock.h"

#define MAX_LOADSTRING 100

HWND        s_hWnd = NULL;
HDC         s_memDC = NULL;
HBITMAP     s_memBitmap = NULL;
void*       s_memBitmapPixel = NULL;
base::Lock  s_memBitmapPixelLock;
HBITMAP     s_memBitmapPrev = NULL;

HINSTANCE hInst;
TCHAR szTitle[MAX_LOADSTRING];
TCHAR szWindowClass[MAX_LOADSTRING];

ATOM                MyRegisterClass(HINSTANCE hInstance);
BOOL                InitInstance(HINSTANCE, int);
LRESULT CALLBACK    WndProc(HWND, UINT, WPARAM, LPARAM);
INT_PTR CALLBACK    About(HWND, UINT, WPARAM, LPARAM);

int APIENTRY _tWinMain(_In_ HINSTANCE hInstance,
                     _In_opt_ HINSTANCE hPrevInstance,
                     _In_ LPTSTR    lpCmdLine,
                     _In_ int       nCmdShow)
{
    UNREFERENCED_PARAMETER(hPrevInstance);
    UNREFERENCED_PARAMETER(lpCmdLine);

    {
        const int nRet = main_win(hInstance);
        if ( 0 <= nRet )
        {
            return nRet;
        }
    }

    MSG msg;
    HACCEL hAccelTable;

    LoadString(hInstance, IDS_APP_TITLE, szTitle, MAX_LOADSTRING);
    LoadString(hInstance, IDC_TEST_CEF_OFFSCREEN, szWindowClass, MAX_LOADSTRING);
    MyRegisterClass(hInstance);

    if (!InitInstance (hInstance, nCmdShow))
    {
        return FALSE;
    }

    hAccelTable = LoadAccelerators(hInstance, MAKEINTRESOURCE(IDC_TEST_CEF_OFFSCREEN));

    while (GetMessage(&msg, NULL, 0, 0))
    {
        if (!TranslateAccelerator(msg.hwnd, hAccelTable, &msg))
        {
            TranslateMessage(&msg);
            DispatchMessage(&msg);
        }
    }

    {
        main_win_term();
    }

    return (int) msg.wParam;
}

ATOM MyRegisterClass(HINSTANCE hInstance)
{
    WNDCLASSEX wcex;

    wcex.cbSize = sizeof(WNDCLASSEX);

    wcex.style          = CS_HREDRAW | CS_VREDRAW;
    wcex.lpfnWndProc    = WndProc;
    wcex.cbClsExtra     = 0;
    wcex.cbWndExtra     = 0;
    wcex.hInstance      = hInstance;
    wcex.hIcon          = LoadIcon(hInstance, MAKEINTRESOURCE(IDI_TEST_CEF_OFFSCREEN));
    wcex.hCursor        = LoadCursor(NULL, IDC_ARROW);
    wcex.hbrBackground  = (HBRUSH)(COLOR_WINDOW+1);
    wcex.lpszMenuName   = MAKEINTRESOURCE(IDC_TEST_CEF_OFFSCREEN);
    wcex.lpszClassName  = szWindowClass;
    wcex.hIconSm        = LoadIcon(wcex.hInstance, MAKEINTRESOURCE(IDI_SMALL));

    return RegisterClassEx(&wcex);
}

BOOL InitInstance(HINSTANCE hInstance, int nCmdShow)
{
   HWND hWnd;

   hInst = hInstance;

   DWORD dwStyle = WS_OVERLAPPEDWINDOW;
   dwStyle ^= WS_MINIMIZEBOX;
   dwStyle ^= WS_MAXIMIZEBOX;
   dwStyle ^= WS_THICKFRAME;

   DWORD dwExStyle = 0;

   int width = CW_USEDEFAULT;
   int height = CW_USEDEFAULT;
   {
       RECT     rect;

       rect.left = 0;
       rect.top = 0;
       rect.bottom = 720;
       rect.right = 1280;

       ::AdjustWindowRectEx(&rect, dwStyle, TRUE, dwExStyle);

       width = rect.right - rect.left;
       height = rect.bottom - rect.top;
   }

   hWnd = CreateWindow(szWindowClass, szTitle, dwStyle,
      CW_USEDEFAULT, 0, width, height, NULL, NULL, hInstance, NULL);

   if (!hWnd)
   {
      return FALSE;
   }

   ShowWindow(hWnd, nCmdShow);
   UpdateWindow(hWnd);

   s_hWnd = hWnd;

   return TRUE;
}

#include "../test_cef_app/cef_client.h"

extern CefRefPtr<Client> s_client;

#include <windowsx.h>

LRESULT CALLBACK WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)
{
    int wmId, wmEvent;
    PAINTSTRUCT ps;
    HDC hdc;

    switch (message)
    {
    case WM_CREATE:
        {
            hdc = GetDC( hWnd );

            s_memDC = CreateCompatibleDC( hdc );
            {
                BITMAPINFO bi;

                ZeroMemory( &bi, sizeof(bi) );
                bi.bmiHeader.biSize = sizeof(BITMAPINFOHEADER);
                bi.bmiHeader.biBitCount = 32;
                bi.bmiHeader.biPlanes = 1;
                bi.bmiHeader.biWidth = 1280;
                bi.bmiHeader.biHeight = -720;

                {
                    base::AutoLock  lock_scope(s_memBitmapPixelLock);

                    s_memBitmap = CreateDIBSection( NULL, &bi, DIB_RGB_COLORS, &s_memBitmapPixel, NULL, 0 );
                    if ( NULL == s_memBitmap )
                    {
                        ReleaseDC( hWnd, hdc );
                        return -1;
                    }

                    {
                        DWORD* p = reinterpret_cast<DWORD*>(s_memBitmapPixel);
                        for ( int x = 0; x < 1280; ++x )
                        {
                            for ( int y = 0; y < 720; ++y )
                            {
                                // A,R,G,B
                                //p[y*1280 + x] = 0xffffffffUL;
                                p[y*1280 + x] = 0x0000ff00UL;
                            }
                        }
                    }
                }
            }
            s_memBitmapPrev = (HBITMAP)SelectObject( s_memDC, s_memBitmap );

            ReleaseDC( hWnd, hdc );
        }
        break;

    case WM_COMMAND:
        wmId    = LOWORD(wParam);
        wmEvent = HIWORD(wParam);
        switch (wmId)
        {
        case IDM_ABOUT:
            DialogBox(hInst, MAKEINTRESOURCE(IDD_ABOUTBOX), hWnd, About);
            break;
        case IDM_EXIT:
            DestroyWindow(hWnd);
            break;
        default:
            return DefWindowProc(hWnd, message, wParam, lParam);
        }
        break;
    case WM_PAINT:
        hdc = BeginPaint(hWnd, &ps);
        BitBlt( hdc, 0, 0, 1280, 720, s_memDC, 0, 0, SRCCOPY );
        EndPaint(hWnd, &ps);
        break;
    case WM_DESTROY:
        {
            if ( NULL != s_memDC )
            {
                if ( NULL != s_memBitmap )
                {
                    base::AutoLock  lock_scope(s_memBitmapPixelLock);

                    SelectObject( s_memDC, s_memBitmapPrev );
                    s_memBitmapPrev = NULL;
                    DeleteObject( s_memBitmap );
                    s_memBitmap = NULL;
                    s_memBitmapPixel = NULL;
                }

                DeleteDC( s_memDC );
                s_memDC = NULL;
            }
        }
        PostQuitMessage(0);
        break;

    case WM_LBUTTONDOWN:
    case WM_LBUTTONUP:
        {
            CefRefPtr<CefBrowser> browser = s_client->getBrowser();
            if ( NULL != browser )
            {
                CefRefPtr<CefBrowserHost> host = browser->GetHost();
                if ( NULL != host )
                {
                    CefMouseEvent event;
                    event.x = GET_X_LPARAM(lParam);
                    event.y = GET_Y_LPARAM(lParam);
                    event.modifiers = 0;
                    const bool btnUp = (WM_LBUTTONUP == message)?(true):(false);
                    host->SendMouseClickEvent( event, MBT_LEFT, btnUp, 1 );
                }
            }
        }
        break;

    case WM_MOUSEMOVE:
        {
            CefRefPtr<CefBrowser> browser = s_client->getBrowser();
            if ( NULL != browser )
            {
                CefRefPtr<CefBrowserHost> host = browser->GetHost();
                if ( NULL != host )
                {
                    CefMouseEvent event;
                    event.x = GET_X_LPARAM(lParam);
                    event.y = GET_Y_LPARAM(lParam);
                    event.modifiers = 0;
                    const bool mouseLeave = false;
                    host->SendMouseMoveEvent( event, mouseLeave );
                }
            }
        }
        break;

    case WM_MOUSEWHEEL:
        {
            CefRefPtr<CefBrowser> browser = s_client->getBrowser();
            if ( NULL != browser )
            {
                CefRefPtr<CefBrowserHost> host = browser->GetHost();
                if ( NULL != host )
                {
                    POINT screenPoint = {
                        GET_X_LPARAM(lParam), GET_Y_LPARAM(lParam)
                    };
                    HWND scrolledWnd = ::WindowFromPoint( screenPoint );
                    if ( hWnd == scrolledWnd )
                    {
                        ::ScreenToClient( hWnd, &screenPoint );
                        const int delta = GET_WHEEL_DELTA_WPARAM(wParam);

                        CefMouseEvent event;
                        event.x = screenPoint.x;
                        event.y = screenPoint.y;
                        event.modifiers = 0;
                        if ( ::GetKeyState( VK_SHIFT ) & 0x8000U )
                        {
                            host->SendMouseWheelEvent( event, delta, 0 );
                        }
                        else
                        {
                            host->SendMouseWheelEvent( event, 0, delta );
                        }
                    }
                }
            }
        }
        break;

    //case WM_XBUTTONDOWN:
    case WM_XBUTTONUP:
        {
            CefRefPtr<CefBrowser> browser = s_client->getBrowser();
            if ( NULL != browser )
            {
                const DWORD fwKeys = GET_KEYSTATE_WPARAM( wParam );
                const DWORD fwButton = GET_XBUTTON_WPARAM( wParam );

                if ( fwButton & XBUTTON1 )
                {
                    if ( browser->CanGoBack() )
                    {
                        browser->GoBack();
                    }
                }
                else
                if ( browser->CanGoForward() )
                {
                    if ( browser->CanGoForward() )
                    {
                        browser->GoForward();
                    }
                }
            }
        }
        break;

    default:
        return DefWindowProc(hWnd, message, wParam, lParam);
    }
    return 0;
}

INT_PTR CALLBACK About(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)
{
    UNREFERENCED_PARAMETER(lParam);
    switch (message)
    {
    case WM_INITDIALOG:
        return (INT_PTR)TRUE;

    case WM_COMMAND:
        if (LOWORD(wParam) == IDOK || LOWORD(wParam) == IDCANCEL)
        {
            EndDialog(hDlg, LOWORD(wParam));
            return (INT_PTR)TRUE;
        }
        break;
    }
    return (INT_PTR)FALSE;
}

<commit_msg>add key event<commit_after>/**
 * The MIT License
 *
 * Copyright (C) 2017 Kiyofumi Kondoh
 *
 * Permission is hereby granted, free of charge, to any person obtaining a copy
 * of this software and associated documentation files (the "Software"), to deal
 * in the Software without restriction, including without limitation the rights
 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
 * copies of the Software, and to permit persons to whom the Software is
 * furnished to do so, subject to the following conditions:
 *
 * The above copyright notice and this permission notice shall be included in
 * all copies or substantial portions of the Software.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
 * THE SOFTWARE.
 */

#include "stdafx.h"
#include "test_cef_offscreen.h"

extern int main_win(HINSTANCE hInstance);
extern int main_win_term();

#include "include/base/cef_lock.h"

#define MAX_LOADSTRING 100

HWND        s_hWnd = NULL;
HDC         s_memDC = NULL;
HBITMAP     s_memBitmap = NULL;
void*       s_memBitmapPixel = NULL;
base::Lock  s_memBitmapPixelLock;
HBITMAP     s_memBitmapPrev = NULL;

HINSTANCE hInst;
TCHAR szTitle[MAX_LOADSTRING];
TCHAR szWindowClass[MAX_LOADSTRING];

ATOM                MyRegisterClass(HINSTANCE hInstance);
BOOL                InitInstance(HINSTANCE, int);
LRESULT CALLBACK    WndProc(HWND, UINT, WPARAM, LPARAM);
INT_PTR CALLBACK    About(HWND, UINT, WPARAM, LPARAM);

int APIENTRY _tWinMain(_In_ HINSTANCE hInstance,
                     _In_opt_ HINSTANCE hPrevInstance,
                     _In_ LPTSTR    lpCmdLine,
                     _In_ int       nCmdShow)
{
    UNREFERENCED_PARAMETER(hPrevInstance);
    UNREFERENCED_PARAMETER(lpCmdLine);

    {
        const int nRet = main_win(hInstance);
        if ( 0 <= nRet )
        {
            return nRet;
        }
    }

    MSG msg;
    HACCEL hAccelTable;

    LoadString(hInstance, IDS_APP_TITLE, szTitle, MAX_LOADSTRING);
    LoadString(hInstance, IDC_TEST_CEF_OFFSCREEN, szWindowClass, MAX_LOADSTRING);
    MyRegisterClass(hInstance);

    if (!InitInstance (hInstance, nCmdShow))
    {
        return FALSE;
    }

    hAccelTable = LoadAccelerators(hInstance, MAKEINTRESOURCE(IDC_TEST_CEF_OFFSCREEN));

    while (GetMessage(&msg, NULL, 0, 0))
    {
        if (!TranslateAccelerator(msg.hwnd, hAccelTable, &msg))
        {
            TranslateMessage(&msg);
            DispatchMessage(&msg);
        }
    }

    {
        main_win_term();
    }

    return (int) msg.wParam;
}

ATOM MyRegisterClass(HINSTANCE hInstance)
{
    WNDCLASSEX wcex;

    wcex.cbSize = sizeof(WNDCLASSEX);

    wcex.style          = CS_HREDRAW | CS_VREDRAW;
    wcex.lpfnWndProc    = WndProc;
    wcex.cbClsExtra     = 0;
    wcex.cbWndExtra     = 0;
    wcex.hInstance      = hInstance;
    wcex.hIcon          = LoadIcon(hInstance, MAKEINTRESOURCE(IDI_TEST_CEF_OFFSCREEN));
    wcex.hCursor        = LoadCursor(NULL, IDC_ARROW);
    wcex.hbrBackground  = (HBRUSH)(COLOR_WINDOW+1);
    wcex.lpszMenuName   = MAKEINTRESOURCE(IDC_TEST_CEF_OFFSCREEN);
    wcex.lpszClassName  = szWindowClass;
    wcex.hIconSm        = LoadIcon(wcex.hInstance, MAKEINTRESOURCE(IDI_SMALL));

    return RegisterClassEx(&wcex);
}

BOOL InitInstance(HINSTANCE hInstance, int nCmdShow)
{
   HWND hWnd;

   hInst = hInstance;

   DWORD dwStyle = WS_OVERLAPPEDWINDOW;
   dwStyle ^= WS_MINIMIZEBOX;
   dwStyle ^= WS_MAXIMIZEBOX;
   dwStyle ^= WS_THICKFRAME;

   DWORD dwExStyle = 0;

   int width = CW_USEDEFAULT;
   int height = CW_USEDEFAULT;
   {
       RECT     rect;

       rect.left = 0;
       rect.top = 0;
       rect.bottom = 720;
       rect.right = 1280;

       ::AdjustWindowRectEx(&rect, dwStyle, TRUE, dwExStyle);

       width = rect.right - rect.left;
       height = rect.bottom - rect.top;
   }

   hWnd = CreateWindow(szWindowClass, szTitle, dwStyle,
      CW_USEDEFAULT, 0, width, height, NULL, NULL, hInstance, NULL);

   if (!hWnd)
   {
      return FALSE;
   }

   ShowWindow(hWnd, nCmdShow);
   UpdateWindow(hWnd);

   s_hWnd = hWnd;

   return TRUE;
}

#include "../test_cef_app/cef_client.h"

extern CefRefPtr<Client> s_client;

#include <windowsx.h>

LRESULT CALLBACK WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)
{
    int wmId, wmEvent;
    PAINTSTRUCT ps;
    HDC hdc;

    switch (message)
    {
    case WM_CREATE:
        {
            hdc = GetDC( hWnd );

            s_memDC = CreateCompatibleDC( hdc );
            {
                BITMAPINFO bi;

                ZeroMemory( &bi, sizeof(bi) );
                bi.bmiHeader.biSize = sizeof(BITMAPINFOHEADER);
                bi.bmiHeader.biBitCount = 32;
                bi.bmiHeader.biPlanes = 1;
                bi.bmiHeader.biWidth = 1280;
                bi.bmiHeader.biHeight = -720;

                {
                    base::AutoLock  lock_scope(s_memBitmapPixelLock);

                    s_memBitmap = CreateDIBSection( NULL, &bi, DIB_RGB_COLORS, &s_memBitmapPixel, NULL, 0 );
                    if ( NULL == s_memBitmap )
                    {
                        ReleaseDC( hWnd, hdc );
                        return -1;
                    }

                    {
                        DWORD* p = reinterpret_cast<DWORD*>(s_memBitmapPixel);
                        for ( int x = 0; x < 1280; ++x )
                        {
                            for ( int y = 0; y < 720; ++y )
                            {
                                // A,R,G,B
                                //p[y*1280 + x] = 0xffffffffUL;
                                p[y*1280 + x] = 0x0000ff00UL;
                            }
                        }
                    }
                }
            }
            s_memBitmapPrev = (HBITMAP)SelectObject( s_memDC, s_memBitmap );

            ReleaseDC( hWnd, hdc );
        }
        break;

    case WM_COMMAND:
        wmId    = LOWORD(wParam);
        wmEvent = HIWORD(wParam);
        switch (wmId)
        {
        case IDM_ABOUT:
            DialogBox(hInst, MAKEINTRESOURCE(IDD_ABOUTBOX), hWnd, About);
            break;
        case IDM_EXIT:
            DestroyWindow(hWnd);
            break;
        default:
            return DefWindowProc(hWnd, message, wParam, lParam);
        }
        break;
    case WM_PAINT:
        hdc = BeginPaint(hWnd, &ps);
        BitBlt( hdc, 0, 0, 1280, 720, s_memDC, 0, 0, SRCCOPY );
        EndPaint(hWnd, &ps);
        break;
    case WM_DESTROY:
        {
            if ( NULL != s_memDC )
            {
                if ( NULL != s_memBitmap )
                {
                    base::AutoLock  lock_scope(s_memBitmapPixelLock);

                    SelectObject( s_memDC, s_memBitmapPrev );
                    s_memBitmapPrev = NULL;
                    DeleteObject( s_memBitmap );
                    s_memBitmap = NULL;
                    s_memBitmapPixel = NULL;
                }

                DeleteDC( s_memDC );
                s_memDC = NULL;
            }
        }
        PostQuitMessage(0);
        break;

    case WM_LBUTTONDOWN:
    case WM_LBUTTONUP:
        {
            CefRefPtr<CefBrowser> browser = s_client->getBrowser();
            if ( NULL != browser )
            {
                CefRefPtr<CefBrowserHost> host = browser->GetHost();
                if ( NULL != host )
                {
                    CefMouseEvent event;
                    event.x = GET_X_LPARAM(lParam);
                    event.y = GET_Y_LPARAM(lParam);
                    event.modifiers = 0;
                    const bool btnUp = (WM_LBUTTONUP == message)?(true):(false);
                    host->SendMouseClickEvent( event, MBT_LEFT, btnUp, 1 );
                }
            }
        }
        break;

    case WM_MOUSEMOVE:
        {
            CefRefPtr<CefBrowser> browser = s_client->getBrowser();
            if ( NULL != browser )
            {
                CefRefPtr<CefBrowserHost> host = browser->GetHost();
                if ( NULL != host )
                {
                    CefMouseEvent event;
                    event.x = GET_X_LPARAM(lParam);
                    event.y = GET_Y_LPARAM(lParam);
                    event.modifiers = 0;
                    const bool mouseLeave = false;
                    host->SendMouseMoveEvent( event, mouseLeave );
                }
            }
        }
        break;

    case WM_MOUSEWHEEL:
        {
            CefRefPtr<CefBrowser> browser = s_client->getBrowser();
            if ( NULL != browser )
            {
                CefRefPtr<CefBrowserHost> host = browser->GetHost();
                if ( NULL != host )
                {
                    POINT screenPoint = {
                        GET_X_LPARAM(lParam), GET_Y_LPARAM(lParam)
                    };
                    HWND scrolledWnd = ::WindowFromPoint( screenPoint );
                    if ( hWnd == scrolledWnd )
                    {
                        ::ScreenToClient( hWnd, &screenPoint );
                        const int delta = GET_WHEEL_DELTA_WPARAM(wParam);

                        CefMouseEvent event;
                        event.x = screenPoint.x;
                        event.y = screenPoint.y;
                        event.modifiers = 0;
                        if ( ::GetKeyState( VK_SHIFT ) & 0x8000U )
                        {
                            host->SendMouseWheelEvent( event, delta, 0 );
                        }
                        else
                        {
                            host->SendMouseWheelEvent( event, 0, delta );
                        }
                    }
                }
            }
        }
        break;

    //case WM_XBUTTONDOWN:
    case WM_XBUTTONUP:
        {
            CefRefPtr<CefBrowser> browser = s_client->getBrowser();
            if ( NULL != browser )
            {
                const DWORD fwKeys = GET_KEYSTATE_WPARAM( wParam );
                const DWORD fwButton = GET_XBUTTON_WPARAM( wParam );

                if ( fwButton & XBUTTON1 )
                {
                    if ( browser->CanGoBack() )
                    {
                        browser->GoBack();
                    }
                }
                else
                if ( browser->CanGoForward() )
                {
                    if ( browser->CanGoForward() )
                    {
                        browser->GoForward();
                    }
                }
            }
        }
        break;

    case WM_KEYDOWN:
    case WM_KEYUP:
        {
            CefRefPtr<CefBrowser> browser = s_client->getBrowser();
            if ( NULL != browser )
            {
                bool needSendKeyEvent = true;

                if (
                    (::GetKeyState( VK_RIGHT ) & 0x8000U )
                    && (::GetKeyState( VK_CONTROL ) & 0x8000U )
                )
                {
                    DCHECK( needSendKeyEvent );
                    if ( browser->CanGoForward() )
                    {
                        browser->GoForward();
                        needSendKeyEvent = false;
                    }
                }

                if (
                    (::GetKeyState( VK_LEFT ) & 0x8000U )
                    && (::GetKeyState( VK_CONTROL ) & 0x8000U )
                )
                {
                    DCHECK( needSendKeyEvent );
                    if ( browser->CanGoBack() )
                    {
                        browser->GoBack();
                        needSendKeyEvent = false;
                    }
                }

                if (
                    (::GetKeyState( VK_F5 ) & 0x8000U )
                )
                {
                    DCHECK( needSendKeyEvent );
                    if ( browser->IsLoading() )
                    {
                        browser->StopLoad();
                    }

                    browser->Reload();
                    needSendKeyEvent = false;
                }

                if ( needSendKeyEvent )
                {
                    CefRefPtr<CefBrowserHost> host = browser->GetHost();
                    if ( NULL != host )
                    {
                        CefKeyEvent event;
                        event.windows_key_code = wParam;
                        event.native_key_code = lParam;
                        event.is_system_key = 0;
                        event.type = (WM_KEYDOWN == message)?(KEYEVENT_RAWKEYDOWN):(KEYEVENT_KEYUP);
                        event.modifiers = 0;

                        if ( ::GetKeyState( VK_CONTROL ) & 0x8000U )
                        {
                            event.modifiers |= EVENTFLAG_CONTROL_DOWN;
                        }
                        if ( ::GetKeyState( VK_SHIFT ) & 0x8000U )
                        {
                            event.modifiers |= EVENTFLAG_SHIFT_DOWN;
                        }

                        host->SendKeyEvent( event );
                    }
                }
            }
        }
        break;

    default:
        return DefWindowProc(hWnd, message, wParam, lParam);
    }
    return 0;
}

INT_PTR CALLBACK About(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)
{
    UNREFERENCED_PARAMETER(lParam);
    switch (message)
    {
    case WM_INITDIALOG:
        return (INT_PTR)TRUE;

    case WM_COMMAND:
        if (LOWORD(wParam) == IDOK || LOWORD(wParam) == IDCANCEL)
        {
            EndDialog(hDlg, LOWORD(wParam));
            return (INT_PTR)TRUE;
        }
        break;
    }
    return (INT_PTR)FALSE;
}

<|endoftext|>


<commit_before>#include "culebra.h"
#include "linenoise.hpp"
#include <fstream>
#include <iomanip>
#include <iostream>
#include <vector>

using namespace culebra;
using namespace peglib;
using namespace std;

bool read_file(const char* path, vector<char>& buff)
{
    ifstream ifs(path, ios::in|ios::binary);
    if (ifs.fail()) {
        return false;
    }

    auto size = static_cast<unsigned int>(ifs.seekg(0, ios::end).tellg());

    if (size > 0) {
        buff.resize(size);
        ifs.seekg(0, ios::beg).read(&buff[0], static_cast<streamsize>(buff.size()));
    }

    return true;
}

struct CommandLineDebugger
{
    void operator()(const Ast& ast, Environment& env, bool force_to_break) {
        if (quit) {
            return;
        }

        if ((command_ == "n" && env.level <= level_) ||
            (command_ == "s") ||
            (command_ == "o" && env.level < level_)) {
            force_to_break = true;
        }

        if (force_to_break) {
            show_lines(ast);

            for (;;) {
                cout << "debug> ";
                string s;
                std::getline(cin, s);

                istringstream is(s);
                is >> command_;

                if (command_ == "h") {
                    cout << "(c)ontinue, (n)ext, (s)tep in, step (o)out, (p)ring, (l)ist, (q)uit" << endl;
                } else if (command_ == "l") {
                    is >> display_lines_;
                    show_lines(ast);
                } else if (command_ == "p") {
                    string symbol;
                    is >> symbol;
                    print(ast, env, symbol);
                } else if (command_ == "c") {
                    break;
                } else if (command_ == "n") {
                    break;
                } else if (command_ == "s") {
                    break;
                } else if (command_ == "o") {
                    break;
                } else if (command_ == "q") {
                    quit = true;
                    break;
                }
            }
            level_ = env.level;;
        }
    }

    void show_lines(const Ast& ast) {
        prepare_cache(ast.path);

        cout << "break in " << ast.path << ":" << ast.line << endl;

        auto count = get_line_count(ast.path);

        auto lines_ahead = (size_t)((display_lines_ - .5) / 2);
        auto start = (size_t)max((int)ast.line - (int)lines_ahead, 1);
        auto end = min(start + display_lines_, count);

        auto needed_digits = to_string(count).length();

        for (auto l = start; l < end; l++) {
            auto s = get_line(ast.path, l);
            if (l == ast.line) {
                cout << "> ";
            } else {
                cout << "  ";
            }
            cout << setw(needed_digits) << l << " " << s << endl;
        }
    }

    shared_ptr<Ast> find_function_node(const Ast& ast) {
        auto node = ast.parent_node;
        while (node->parent_node && node->tag != "FUNCTION"_) {
            node = node->parent_node;
        }
        return node;
    }

    void enum_identifiers(const Ast& ast, set<string>& references) {
        for (auto node: ast.nodes) {
            switch (node->tag) {
            case "IDENTIFIER"_:
                references.insert(node->token);
                break;
            case "FUNCTION"_:
                break;
            default:
                enum_identifiers(*node, references);
                break;
            }
        }
    }

    void print(const Ast& ast, Environment& env, const string& symbol) {
        if (symbol.empty()) {
            print_all(ast, env);
        } else if (env.has(symbol)) {
            cout << symbol << ": " << env.get(symbol).str() << endl;
        } else {
            cout << "'" << symbol << "'" << "is not undefined." << endl;
        }
    }

    void print_all(const Ast& ast, Environment& env) {
        auto node = find_function_node(ast);
        set<string> references;
        enum_identifiers(*node, references);
        for (const auto& symbol: references) {
            const auto& val = env.get(symbol);
            cout << symbol << ": " << val.str() << endl;
        }
    }

    size_t get_line_count(const string& path) {
        return sources_[path].size();
    }

    string get_line(const string& path, size_t line) {
        const auto& positions = sources_[path];
        auto idx = line - 1;
        auto first = idx > 0 ? positions[idx - 1] : 0;
        auto last = positions[idx];
        auto size = last - first;

        string s(size, 0);
        ifstream ifs(path, ios::in | ios::binary);
        ifs.seekg(first, ios::beg).read((char*)s.data(), static_cast<streamsize>(s.size()));

        size_t count = 0;
        auto rit = s.rbegin();
        while (rit != s.rend()) {
            if (*rit == '\n') {
                count++;
            }
            ++rit;
        }

        s = s.substr(0, s.size() - count);

        return s;
    }

    void prepare_cache(const string& path) {
        auto it = sources_.find(path);
        if (it == sources_.end()) {
            vector<char> buff;
            read_file(path.c_str(), buff);

            auto& positions = sources_[path];

            auto i = 0u;
            for (; i < buff.size(); i++) {
                if (buff[i] == '\n') {
                    positions.push_back(i + 1);
                }
            }
            positions.push_back(i);
        }
    }

    bool   quit = false;
    string command_;
    size_t level_ = 0;
    size_t display_lines_ = 4;
    map<string, vector<size_t>> sources_;
};

int repl(shared_ptr<Environment> env, bool print_ast)
{
    for (;;) {
        auto line = linenoise::Readline("cul> ");

        if (line == "exit" || line == "quit") {
            break;
        }

        if (!line.empty()) {
            Value           val;
            vector<string>  msgs;
            shared_ptr<Ast> ast;
            auto ret = run("(repl)", env, line.c_str(), line.size(), val, msgs, ast);
            if (ret) {
                if (print_ast) {
                    AstPrint::print(ast);
                }
                cout << val << endl;
                linenoise::AddHistory(line.c_str());
            } else if (!msgs.empty()) {
                for (const auto& msg: msgs) {
                    cout << msg << endl;;
                }
            }
        }
    }

    return 0;
}

int main(int argc, const char** argv)
{
    auto print_ast = false;
    auto shell = false;
    auto debug = false;
    vector<const char*> path_list;

    int argi = 1;
    while (argi < argc) {
        auto arg = argv[argi++];
        if (string("--shell") == arg) {
            shell = true;
        } else if (string("--ast") == arg) {
            print_ast = true;
        } else if (string("--debug") == arg) {
            debug = true;
        } else {
            path_list.push_back(arg);
        }
    }

    if (!shell) {
        shell = path_list.empty();
    }

    try {
        auto env = make_shared<Environment>();
        setup_built_in_functions(*env);

        for (auto path: path_list) {
            vector<char> buff;
            if (!read_file(path, buff)) {
                cerr << "can't open '" << path << "'." << endl;
                return -1;
            }

            Value           val;
            vector<string>  msgs;
            shared_ptr<Ast> ast;
            Debugger        dbg;

            CommandLineDebugger debugger;
            if (debug) {
                dbg = debugger;
            }

            auto ret = run(path, env, buff.data(), buff.size(), val, msgs, ast, dbg);

            if (!ret) {
                for (const auto& msg: msgs) {
                    cerr << msg << endl;
                }
                return -1;
            } else if (print_ast) {
                AstPrint::print(ast);
            }
        }

        if (shell) {
            repl(env, print_ast);
        }
    } catch (exception& e) {
        cerr << e.what() << endl;
        return -1;
    }

    return 0;
}

// vim: et ts=4 sw=4 cin cino={1s ff=unix
<commit_msg>Fixed debuger problems.<commit_after>#include "culebra.h"
#include "linenoise.hpp"
#include <fstream>
#include <iomanip>
#include <iostream>
#include <vector>

using namespace culebra;
using namespace peglib;
using namespace std;

bool read_file(const char* path, vector<char>& buff)
{
    ifstream ifs(path, ios::in|ios::binary);
    if (ifs.fail()) {
        return false;
    }

    auto size = static_cast<unsigned int>(ifs.seekg(0, ios::end).tellg());

    if (size > 0) {
        buff.resize(size);
        ifs.seekg(0, ios::beg).read(&buff[0], static_cast<streamsize>(buff.size()));
    }

    return true;
}

struct CommandLineDebugger
{
    void operator()(const Ast& ast, Environment& env, bool force_to_break) {
        if (quit) {
            return;
        }

        if ((command_ == "n" && env.level <= level_) ||
            (command_ == "s") ||
            (command_ == "o" && env.level < level_)) {
            force_to_break = true;
        }

        if (force_to_break) {
            show_lines(ast);

            for (;;) {
                cout << "debug> ";
                string s;
                std::getline(cin, s);

                istringstream is(s);
                is >> command_;

                if (command_ == "h") {
                    cout << "(c)ontinue, (n)ext, (s)tep in, step (o)out, (p)ring, (l)ist, (q)uit" << endl;
                } else if (command_ == "l") {
                    is >> display_lines_;
                    show_lines(ast);
                } else if (command_ == "p") {
                    string symbol;
                    is >> symbol;
                    print(ast, env, symbol);
                } else if (command_ == "c") {
                    break;
                } else if (command_ == "n") {
                    break;
                } else if (command_ == "s") {
                    break;
                } else if (command_ == "o") {
                    break;
                } else if (command_ == "q") {
                    quit = true;
                    break;
                }
            }
            level_ = env.level;;
        }
    }

    void show_lines(const Ast& ast) {
        prepare_cache(ast.path);

        cout << "break in " << ast.path << ":" << ast.line << endl;

        auto count = get_line_count(ast.path);

        auto lines_ahead = (size_t)((display_lines_ - .5) / 2);
        auto start = (size_t)max((int)ast.line - (int)lines_ahead, 1);
        auto end = min(start + display_lines_, count);

        auto needed_digits = to_string(count).length();

        for (auto l = start; l < end; l++) {
            auto s = get_line(ast.path, l);
            if (l == ast.line) {
                cout << "> ";
            } else {
                cout << "  ";
            }
            cout << setw(needed_digits) << l << " " << s << endl;
        }
    }

    shared_ptr<Ast> find_function_node(const Ast& ast) {
        auto node = ast.parent;
        while (node->parent && node->tag != "FUNCTION"_) {
            node = node->parent;
        }
        return node;
    }

    void enum_identifiers(const Ast& ast, set<string>& references) {
        for (auto node: ast.nodes) {
            switch (node->tag) {
            case "IDENTIFIER"_:
                references.insert(node->token);
                break;
            case "FUNCTION"_:
                break;
            default:
                enum_identifiers(*node, references);
                break;
            }
        }
    }

    void print(const Ast& ast, Environment& env, const string& symbol) {
        if (symbol.empty()) {
            print_all(ast, env);
        } else if (env.has(symbol)) {
            cout << symbol << ": " << env.get(symbol).str() << endl;
        } else {
            cout << "'" << symbol << "'" << "is not undefined." << endl;
        }
    }

    void print_all(const Ast& ast, Environment& env) {
        auto node = find_function_node(ast);
        set<string> references;
        enum_identifiers(*node, references);
        for (const auto& symbol: references) {
            if (env.has(symbol)) {
                const auto& val = env.get(symbol);
                if (val.type != Value::Function) {
                    cout << symbol << ": " << val.str() << endl;
                }
            }
        }
    }

    size_t get_line_count(const string& path) {
        return sources_[path].size();
    }

    string get_line(const string& path, size_t line) {
        const auto& positions = sources_[path];
        auto idx = line - 1;
        auto first = idx > 0 ? positions[idx - 1] : 0;
        auto last = positions[idx];
        auto size = last - first;

        string s(size, 0);
        ifstream ifs(path, ios::in | ios::binary);
        ifs.seekg(first, ios::beg).read((char*)s.data(), static_cast<streamsize>(s.size()));

        size_t count = 0;
        auto rit = s.rbegin();
        while (rit != s.rend()) {
            if (*rit == '\n') {
                count++;
            }
            ++rit;
        }

        s = s.substr(0, s.size() - count);

        return s;
    }

    void prepare_cache(const string& path) {
        auto it = sources_.find(path);
        if (it == sources_.end()) {
            vector<char> buff;
            read_file(path.c_str(), buff);

            auto& positions = sources_[path];

            auto i = 0u;
            for (; i < buff.size(); i++) {
                if (buff[i] == '\n') {
                    positions.push_back(i + 1);
                }
            }
            positions.push_back(i);
        }
    }

    bool   quit = false;
    string command_;
    size_t level_ = 0;
    size_t display_lines_ = 4;
    map<string, vector<size_t>> sources_;
};

int repl(shared_ptr<Environment> env, bool print_ast)
{
    for (;;) {
        auto line = linenoise::Readline("cul> ");

        if (line == "exit" || line == "quit") {
            break;
        }

        if (!line.empty()) {
            Value           val;
            vector<string>  msgs;
            shared_ptr<Ast> ast;
            auto ret = run("(repl)", env, line.c_str(), line.size(), val, msgs, ast);
            if (ret) {
                if (print_ast) {
                    AstPrint::print(ast);
                }
                cout << val << endl;
                linenoise::AddHistory(line.c_str());
            } else if (!msgs.empty()) {
                for (const auto& msg: msgs) {
                    cout << msg << endl;;
                }
            }
        }
    }

    return 0;
}

int main(int argc, const char** argv)
{
    auto print_ast = false;
    auto shell = false;
    auto debug = false;
    vector<const char*> path_list;

    int argi = 1;
    while (argi < argc) {
        auto arg = argv[argi++];
        if (string("--shell") == arg) {
            shell = true;
        } else if (string("--ast") == arg) {
            print_ast = true;
        } else if (string("--debug") == arg) {
            debug = true;
        } else {
            path_list.push_back(arg);
        }
    }

    if (!shell) {
        shell = path_list.empty();
    }

    try {
        auto env = make_shared<Environment>();
        setup_built_in_functions(*env);

        for (auto path: path_list) {
            vector<char> buff;
            if (!read_file(path, buff)) {
                cerr << "can't open '" << path << "'." << endl;
                return -1;
            }

            Value           val;
            vector<string>  msgs;
            shared_ptr<Ast> ast;
            Debugger        dbg;

            CommandLineDebugger debugger;
            if (debug) {
                dbg = debugger;
            }

            auto ret = run(path, env, buff.data(), buff.size(), val, msgs, ast, dbg);

            if (!ret) {
                for (const auto& msg: msgs) {
                    cerr << msg << endl;
                }
                return -1;
            } else if (print_ast) {
                AstPrint::print(ast);
            }
        }

        if (shell) {
            repl(env, print_ast);
        }
    } catch (exception& e) {
        cerr << e.what() << endl;
        return -1;
    }

    return 0;
}

// vim: et ts=4 sw=4 cin cino={1s ff=unix
<|endoftext|>


<commit_before>// Copyright 2012-2013 Samplecount S.L.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

#ifndef METHCLA_ENGINE_HPP_INCLUDED
#define METHCLA_ENGINE_HPP_INCLUDED

#include <methcla/engine.h>
#include <methcla/lv2/atom.hpp>

#include <future>
#include <iostream>
#include <memory>
#include <stdexcept>
#include <string>
#include <thread>
#include <unordered_map>

#include <boost/optional.hpp>
#include <boost/serialization/strong_typedef.hpp>
#include <boost/variant.hpp>
#include <boost/variant/apply_visitor.hpp>

#include <oscpp/client.hpp>
#include <oscpp/server.hpp>

namespace Methcla
{
    inline static void dumpMessage(std::ostream& out, const OSC::Server::Message& msg)
    {
        out << msg.address() << ' ';
        OSC::Server::ArgStream args(msg.args());
        while (!args.atEnd()) {
            const char t = args.tag();
            out << t << ':';
            switch (t) {
                case 'i':
                    out << args.int32();
                    break;
                case 'f':
                    out << args.float32();
                    break;
                case 's':
                    out << args.string();
                    break;
                case 'b':
                    out << args.blob().size;
                    break;
                default:
                    out << '?';
                    break;
            }
            out << ' ';
        }
    }

    inline static void dumpRequest(std::ostream& out, const OSC::Server::Packet& packet)
    {
        out << "Request (send): ";
        dumpMessage(out, packet);
        out << std::endl;
    }

    inline static std::exception_ptr responseToException(const OSC::Server::Packet& packet)
    {
        if (packet.isMessage()) {
            OSC::Server::Message msg(packet);
            if (msg == "/error") {
                auto args(msg.args());
                args.drop(); // request id
                const char* error = args.string();
                return std::make_exception_ptr(std::runtime_error(error));
            } else {
                return std::exception_ptr();
            }
        } else {
            return std::make_exception_ptr(std::invalid_argument("Response is not a message"));
        }
    }

    BOOST_STRONG_TYPEDEF(int32_t, SynthId);
    BOOST_STRONG_TYPEDEF(int32_t, AudioBusId);

    template <class T> class ExceptionVisitor : public boost::static_visitor<T>
    {
    public:
        T operator()(const std::exception_ptr& e) const
        {
            std::rethrow_exception(e);
        }

        T operator()(const T& x) const
        {
            return x;
        }
    };

    namespace impl
    {
        struct Result : boost::noncopyable
        {
            Result()
                : m_cond(false)
            { }

            inline void notify()
            {
                m_cond = true;
                m_cond_var.notify_one();
            }

            inline void wait()
            {
                std::unique_lock<std::mutex> lock(m_mutex);
                while (!m_cond) {
                    m_cond_var.wait(lock);
                }
                if (m_exc) {
                    std::rethrow_exception(m_exc);
                }
            }

            void set_exception(std::exception_ptr exc)
            {
                BOOST_ASSERT(m_cond);
                std::lock_guard<std::mutex> lock(m_mutex);
                m_exc = exc;
                notify();
            }

            std::mutex m_mutex;
            std::condition_variable m_cond_var;
            bool m_cond;
            std::exception_ptr m_exc;
        };
    };

    template <class T> class Result : impl::Result
    {
    public:
        void set(std::exception_ptr exc)
        {
            set_exception(exc);
        }

        void set(const T& value)
        {
            BOOST_ASSERT(!m_cond);
            std::lock_guard<std::mutex> lock(m_mutex);
            m_value = value;
            notify();
        }

        const T& get()
        {
            wait();
            return m_value;
        }

    private:
        T m_value;
    };

    template <> class Result<void> : impl::Result
    {
    public:
        void set(std::exception_ptr exc)
        {
            set_exception(exc);
        }

        void set()
        {
            BOOST_ASSERT(!m_cond);
            std::lock_guard<std::mutex> lock(m_mutex);
            notify();
        }

        void get()
        {
            wait();
        }
    };

    template <typename T> bool checkResponse(const OSC::Server::Packet& response, Result<T>& result)
    {
        auto error = responseToException(response);
        if (error) {
            result.set(error);
            return false;
        }
        return true;
    }

    class Engine
    {
    public:
        Engine(const Methcla_Option* options)
            : m_engine(methcla_engine_new(handlePacket, this, options))
            , m_requestId(kMethcla_Notification+1)
        {
            check(m_engine);
        }
        ~Engine()
        {
            methcla_engine_free(m_engine);
        }

        void* impl()
        {
            return methcla_engine_impl(m_engine);
        }

        void start()
        {
            methcla_engine_start(m_engine);
            check(m_engine);
        }

        void stop()
        {
            methcla_engine_stop(m_engine);
            check(m_engine);
        }

        SynthId synth(const char* synthDef)
        {
            const char address[] = "/s_new";
            const size_t numArgs = 4;
            const size_t packetSize = OSC::Size::message(address, numArgs)
                                         + OSC::Size::int32()
                                         + OSC::Size::string(256)
                                         + 2 * OSC::Size::int32();

            const Methcla_RequestId requestId = getRequestId();

            OSC::Client::StaticPacket<packetSize> request;
            request
                .openMessage(address, numArgs)
                    .int32(requestId)
                    .string(synthDef)
                    .int32(0)
                    .int32(0)
                .closeMessage();

            dumpRequest(std::cerr, OSC::Server::Packet(request.data(), request.size()));

            Result<SynthId> result;

            withRequest(requestId, request, [&result](Methcla_RequestId requestId, const void* buffer, size_t size){
                OSC::Server::Packet response(buffer, size);
                if (checkResponse(response, result)) {
                    auto args = ((OSC::Server::Message)response).args();
                    int32_t requestId_ = args.int32();
                    BOOST_ASSERT_MSG( requestId_ == requestId, "Request id mismatch");
                    int32_t nodeId = args.int32();
                    std::cerr << "synth: " << requestId << " " << nodeId << std::endl;
                    result.set(SynthId(nodeId));
                }
            });

            return result.get();
        }

        void mapOutput(const SynthId& synth, size_t index, AudioBusId bus)
        {
            const char address[] = "/synth/map/output";
            const size_t numArgs = 4;
            const size_t packetSize =  OSC::Size::message(address, numArgs)
                                         + numArgs * OSC::Size::int32();

            Methcla_RequestId requestId = getRequestId();

            OSC::Client::StaticPacket<packetSize> request;
            request
                .openMessage(address, numArgs)
                    .int32(requestId)
                    .int32(synth)
                    .int32(index)
                    .int32(bus)
                .closeMessage();

            dumpRequest(std::cerr, OSC::Server::Packet(request.data(), request.size()));

            Result<void> result;

            withRequest(requestId, request, [&result](Methcla_RequestId requestId, const void* buffer, size_t size){
                if (checkResponse(OSC::Server::Packet(buffer, size), result)) {
                    result.set();
                }
            });

            result.get();
        }

    private:
        static void check(const Methcla_Engine* engine)
        {
            Methcla_Error err = methcla_engine_error(engine);
            if (err != kMethcla_NoError) {
                const char* msg = methcla_engine_error_message(engine);
                if (err == kMethcla_InvalidArgument)
                    throw std::invalid_argument(msg);
                else if (err == kMethcla_BadAlloc)
                    throw std::bad_alloc();
                else
                    throw std::runtime_error(msg);
            }
        }

        static void handlePacket(void* data, Methcla_RequestId requestId, const void* packet, size_t size)
        {
            static_cast<Engine*>(data)->handlePacket(requestId, packet, size);
        }

        void handlePacket(Methcla_RequestId requestId, const void* packet, size_t size)
        {
            std::lock_guard<std::mutex> lock(m_callbacksMutex);
            // look up request id and invoke callback
            auto it = m_callbacks.find(requestId);
            if (it != m_callbacks.end()) {
                it->second(requestId, packet, size);
                m_callbacks.erase(it);
            }
        }

        void send(const void* packet, size_t size)
        {
            methcla_engine_send(m_engine, packet, size);
        }

        void send(const OSC::Client::Packet& packet)
        {
            send(packet.data(), packet.size());
        }

        Methcla_RequestId getRequestId()
        {
            std::lock_guard<std::mutex> lock(m_requestIdMutex);
            Methcla_RequestId result = m_requestId;
            if (result == kMethcla_Notification) {
                result++;
            }
            m_requestId = result + 1;
            return result;
        }

        void registerResponse(Methcla_RequestId requestId, std::function<void (Methcla_RequestId, const void*, size_t)> callback)
        {
            std::lock_guard<std::mutex> lock(m_callbacksMutex);
            BOOST_ASSERT_MSG( m_callbacks.find(requestId) == m_callbacks.end(), "Duplicate request id" );
            m_callbacks[requestId] = callback;
        }

        void withRequest(Methcla_RequestId requestId, const OSC::Client::Packet& request, std::function<void (Methcla_RequestId, const void*, size_t)> callback)
        {
            registerResponse(requestId, callback);
            send(request);
        }

    private:
        Methcla_Engine*     m_engine;
        Methcla_RequestId   m_requestId;
        std::mutex          m_requestIdMutex;
        std::unordered_map<Methcla_RequestId,std::function<void (Methcla_RequestId, const void*, size_t)>> m_callbacks;
        std::mutex          m_callbacksMutex;
    };
};

#endif // METHCLA_ENGINE_HPP_INCLUDED
<commit_msg>Whitespace fix<commit_after>// Copyright 2012-2013 Samplecount S.L.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

#ifndef METHCLA_ENGINE_HPP_INCLUDED
#define METHCLA_ENGINE_HPP_INCLUDED

#include <methcla/engine.h>
#include <methcla/lv2/atom.hpp>

#include <future>
#include <iostream>
#include <memory>
#include <stdexcept>
#include <string>
#include <thread>
#include <unordered_map>

#include <boost/optional.hpp>
#include <boost/serialization/strong_typedef.hpp>
#include <boost/variant.hpp>
#include <boost/variant/apply_visitor.hpp>

#include <oscpp/client.hpp>
#include <oscpp/server.hpp>

namespace Methcla
{
    inline static void dumpMessage(std::ostream& out, const OSC::Server::Message& msg)
    {
        out << msg.address() << ' ';
        OSC::Server::ArgStream args(msg.args());
        while (!args.atEnd()) {
            const char t = args.tag();
            out << t << ':';
            switch (t) {
                case 'i':
                    out << args.int32();
                    break;
                case 'f':
                    out << args.float32();
                    break;
                case 's':
                    out << args.string();
                    break;
                case 'b':
                    out << args.blob().size;
                    break;
                default:
                    out << '?';
                    break;
            }
            out << ' ';
        }
    }

    inline static void dumpRequest(std::ostream& out, const OSC::Server::Packet& packet)
    {
        out << "Request (send): ";
        dumpMessage(out, packet);
        out << std::endl;
    }

    inline static std::exception_ptr responseToException(const OSC::Server::Packet& packet)
    {
        if (packet.isMessage()) {
            OSC::Server::Message msg(packet);
            if (msg == "/error") {
                auto args(msg.args());
                args.drop(); // request id
                const char* error = args.string();
                return std::make_exception_ptr(std::runtime_error(error));
            } else {
                return std::exception_ptr();
            }
        } else {
            return std::make_exception_ptr(std::invalid_argument("Response is not a message"));
        }
    }

    BOOST_STRONG_TYPEDEF(int32_t, SynthId);
    BOOST_STRONG_TYPEDEF(int32_t, AudioBusId);

    template <class T> class ExceptionVisitor : public boost::static_visitor<T>
    {
    public:
        T operator()(const std::exception_ptr& e) const
        {
            std::rethrow_exception(e);
        }

        T operator()(const T& x) const
        {
            return x;
        }
    };

    namespace impl
    {
        struct Result : boost::noncopyable
        {
            Result()
                : m_cond(false)
            { }

            inline void notify()
            {
                m_cond = true;
                m_cond_var.notify_one();
            }

            inline void wait()
            {
                std::unique_lock<std::mutex> lock(m_mutex);
                while (!m_cond) {
                    m_cond_var.wait(lock);
                }
                if (m_exc) {
                    std::rethrow_exception(m_exc);
                }
            }

            void set_exception(std::exception_ptr exc)
            {
                BOOST_ASSERT(m_cond);
                std::lock_guard<std::mutex> lock(m_mutex);
                m_exc = exc;
                notify();
            }

            std::mutex m_mutex;
            std::condition_variable m_cond_var;
            bool m_cond;
            std::exception_ptr m_exc;
        };
    };

    template <class T> class Result : impl::Result
    {
    public:
        void set(std::exception_ptr exc)
        {
            set_exception(exc);
        }

        void set(const T& value)
        {
            BOOST_ASSERT(!m_cond);
            std::lock_guard<std::mutex> lock(m_mutex);
            m_value = value;
            notify();
        }

        const T& get()
        {
            wait();
            return m_value;
        }

    private:
        T m_value;
    };

    template <> class Result<void> : impl::Result
    {
    public:
        void set(std::exception_ptr exc)
        {
            set_exception(exc);
        }

        void set()
        {
            BOOST_ASSERT(!m_cond);
            std::lock_guard<std::mutex> lock(m_mutex);
            notify();
        }

        void get()
        {
            wait();
        }
    };

    template <typename T> bool checkResponse(const OSC::Server::Packet& response, Result<T>& result)
    {
        auto error = responseToException(response);
        if (error) {
            result.set(error);
            return false;
        }
        return true;
    }

    class Engine
    {
    public:
        Engine(const Methcla_Option* options)
            : m_engine(methcla_engine_new(handlePacket, this, options))
            , m_requestId(kMethcla_Notification+1)
        {
            check(m_engine);
        }
        ~Engine()
        {
            methcla_engine_free(m_engine);
        }

        void* impl()
        {
            return methcla_engine_impl(m_engine);
        }

        void start()
        {
            methcla_engine_start(m_engine);
            check(m_engine);
        }

        void stop()
        {
            methcla_engine_stop(m_engine);
            check(m_engine);
        }

        SynthId synth(const char* synthDef)
        {
            const char address[] = "/s_new";
            const size_t numArgs = 4;
            const size_t packetSize = OSC::Size::message(address, numArgs)
                                         + OSC::Size::int32()
                                         + OSC::Size::string(256)
                                         + 2 * OSC::Size::int32();

            const Methcla_RequestId requestId = getRequestId();

            OSC::Client::StaticPacket<packetSize> request;
            request
                .openMessage(address, numArgs)
                    .int32(requestId)
                    .string(synthDef)
                    .int32(0)
                    .int32(0)
                .closeMessage();

            dumpRequest(std::cerr, OSC::Server::Packet(request.data(), request.size()));

            Result<SynthId> result;

            withRequest(requestId, request, [&result](Methcla_RequestId requestId, const void* buffer, size_t size){
                OSC::Server::Packet response(buffer, size);
                if (checkResponse(response, result)) {
                    auto args = ((OSC::Server::Message)response).args();
                    int32_t requestId_ = args.int32();
                    BOOST_ASSERT_MSG( requestId_ == requestId, "Request id mismatch");
                    int32_t nodeId = args.int32();
                    std::cerr << "synth: " << requestId << " " << nodeId << std::endl;
                    result.set(SynthId(nodeId));
                }
            });

            return result.get();
        }

        void mapOutput(const SynthId& synth, size_t index, AudioBusId bus)
        {
            const char address[] = "/synth/map/output";
            const size_t numArgs = 4;
            const size_t packetSize = OSC::Size::message(address, numArgs)
                                        + numArgs * OSC::Size::int32();

            Methcla_RequestId requestId = getRequestId();

            OSC::Client::StaticPacket<packetSize> request;
            request
                .openMessage(address, numArgs)
                    .int32(requestId)
                    .int32(synth)
                    .int32(index)
                    .int32(bus)
                .closeMessage();

            dumpRequest(std::cerr, OSC::Server::Packet(request.data(), request.size()));

            Result<void> result;

            withRequest(requestId, request, [&result](Methcla_RequestId requestId, const void* buffer, size_t size){
                if (checkResponse(OSC::Server::Packet(buffer, size), result)) {
                    result.set();
                }
            });

            result.get();
        }

    private:
        static void check(const Methcla_Engine* engine)
        {
            Methcla_Error err = methcla_engine_error(engine);
            if (err != kMethcla_NoError) {
                const char* msg = methcla_engine_error_message(engine);
                if (err == kMethcla_InvalidArgument)
                    throw std::invalid_argument(msg);
                else if (err == kMethcla_BadAlloc)
                    throw std::bad_alloc();
                else
                    throw std::runtime_error(msg);
            }
        }

        static void handlePacket(void* data, Methcla_RequestId requestId, const void* packet, size_t size)
        {
            static_cast<Engine*>(data)->handlePacket(requestId, packet, size);
        }

        void handlePacket(Methcla_RequestId requestId, const void* packet, size_t size)
        {
            std::lock_guard<std::mutex> lock(m_callbacksMutex);
            // look up request id and invoke callback
            auto it = m_callbacks.find(requestId);
            if (it != m_callbacks.end()) {
                it->second(requestId, packet, size);
                m_callbacks.erase(it);
            }
        }

        void send(const void* packet, size_t size)
        {
            methcla_engine_send(m_engine, packet, size);
        }

        void send(const OSC::Client::Packet& packet)
        {
            send(packet.data(), packet.size());
        }

        Methcla_RequestId getRequestId()
        {
            std::lock_guard<std::mutex> lock(m_requestIdMutex);
            Methcla_RequestId result = m_requestId;
            if (result == kMethcla_Notification) {
                result++;
            }
            m_requestId = result + 1;
            return result;
        }

        void registerResponse(Methcla_RequestId requestId, std::function<void (Methcla_RequestId, const void*, size_t)> callback)
        {
            std::lock_guard<std::mutex> lock(m_callbacksMutex);
            BOOST_ASSERT_MSG( m_callbacks.find(requestId) == m_callbacks.end(), "Duplicate request id" );
            m_callbacks[requestId] = callback;
        }

        void withRequest(Methcla_RequestId requestId, const OSC::Client::Packet& request, std::function<void (Methcla_RequestId, const void*, size_t)> callback)
        {
            registerResponse(requestId, callback);
            send(request);
        }

    private:
        Methcla_Engine*     m_engine;
        Methcla_RequestId   m_requestId;
        std::mutex          m_requestIdMutex;
        std::unordered_map<Methcla_RequestId,std::function<void (Methcla_RequestId, const void*, size_t)>> m_callbacks;
        std::mutex          m_callbacksMutex;
    };
};

#endif // METHCLA_ENGINE_HPP_INCLUDED
<|endoftext|>


<commit_before>// This file is part of the dune-pymor project:
//   https://github.com/pymor/dune-pymor
// Copyright holders: Stephan Rave, Felix Schindler
// License: BSD 2-Clause License (http://opensource.org/licenses/BSD-2-Clause)

#ifndef DUNE_PYMOR_OPERATORS_INTERFACES_HH
#define DUNE_PYMOR_OPERATORS_INTERFACES_HH

#include <dune/stuff/common/configuration.hh>
#include <dune/stuff/common/crtp.hh>
#include <dune/stuff/common/profiler.hh>
#include <dune/stuff/common/type_utils.hh>
#include <dune/stuff/la/container/interfaces.hh>
#include <dune/stuff/la/solver.hh>

#include <dune/pymor/common/exceptions.hh>
#include <dune/pymor/parameters/base.hh>
#include <dune/pymor/parameters/functional.hh>

namespace Dune {
namespace Pymor {

/**
 *  \brief  Contains tags mostly needed for python bindings.
 */
namespace Tags {

class OperatorInterface {};

class AffinelyDecomposedOperatorInterface : public OperatorInterface {};

} // namespace Tags

template< class Traits >
class OperatorInterface
  : public Parametric
  , public Tags::OperatorInterface
  , public Stuff::CRTPInterface< OperatorInterface< Traits >, Traits >
{
public:
  typedef typename Traits::derived_type derived_type;
  typedef typename Traits::SourceType   SourceType;
  typedef typename Traits::RangeType    RangeType;
  typedef typename Traits::ScalarType   ScalarType;
  typedef typename Traits::FrozenType   FrozenType;
  typedef typename Traits::InverseType  InverseType;

  static_assert(std::is_base_of< Stuff::LA::VectorInterface< typename SourceType::Traits >, SourceType >::value,
                "SourceType has to be derived from Stuff::LA::VectorInterface!");
  static_assert(std::is_base_of< Stuff::LA::VectorInterface< typename RangeType::Traits >, RangeType >::value,
                "RangeType has to be derived from Stuff::LA::VectorInterface!");

  static std::string static_id() {    return "pymor.operators.interface"; }
  static std::string type_this() {    return Stuff::Common::Typename< derived_type >::value(); }
  static std::string type_source() {  return Stuff::Common::Typename< SourceType >::value(); }
  static std::string type_range() {   return Stuff::Common::Typename< RangeType >::value(); }
  static std::string type_scalar() {  return Stuff::Common::Typename< ScalarType >::value(); }
  static std::string type_frozen() {  return Stuff::Common::Typename< FrozenType >::value(); }
  static std::string type_inverse() { return Stuff::Common::Typename< InverseType >::value(); }

  OperatorInterface(const ParameterType mu = ParameterType())
    : Parametric(mu)
  {}

  OperatorInterface(const Parametric& other)
    : Parametric(other)
  {}

  bool linear() const
  {
    CHECK_CRTP(this->as_imp(*this).linear());
    return this->as_imp(*this).linear();
  }

  DUNE_STUFF_SSIZE_T dim_source() const
  {
    CHECK_CRTP(this->as_imp(*this).dim_source());
    return this->as_imp(*this).dim_source();
  }

  DUNE_STUFF_SSIZE_T dim_range() const
  {
    CHECK_CRTP(this->as_imp(*this).dim_range());
    return this->as_imp(*this).dim_range();
  }

  void apply(const SourceType& source, RangeType& range, const Parameter mu = Parameter()) const
  {
    DUNE_STUFF_PROFILE_SCOPE(static_id() + ".apply__source_range_mu)");
    CHECK_AND_CALL_CRTP(this->as_imp(*this).apply(source, range, mu));
  }

  RangeType apply(const SourceType& source, const Parameter mu = Parameter()) const
  {
    DUNE_STUFF_PROFILE_SCOPE(static_id() + ".apply__source_mu)");
    RangeType range(dim_range());
    apply(source, range, mu);
    return range;
  }

  RangeType* apply_and_return_ptr(const SourceType& source, const Parameter mu = Parameter()) const
  {
    DUNE_STUFF_PROFILE_SCOPE(static_id() + ".apply_and_return_ptr");
    return new RangeType(apply(source, mu));
  }

  /**
   * \note  This default implementation of apply2 creates a temporary vector. Any derived class which can do better
   *        should implement this method!
   */
  ScalarType apply2(const RangeType& range, const SourceType& source, const Parameter mu = Parameter()) const
  {
    RangeType tmp = range.copy();
    apply(source, tmp, mu);
    return tmp.dot(range);
  }

  static std::vector< std::string > invert_options()
  {
    return derived_type::invert_options();
  }

  static Stuff::Common::Configuration invert_options(const std::string& type)
  {
    return derived_type::invert_options(type);
  }

  InverseType invert(const std::string type = invert_options()[0], const Parameter mu = Parameter()) const
  {
    return invert(invert_options(type), mu);
  }

  InverseType invert(const Stuff::Common::Configuration& option, const Parameter mu = Parameter()) const
  {
    CHECK_CRTP(this->as_imp(*this).invert(option, mu));
    return this->as_imp(*this).invert(option, mu);
  }

  InverseType* invert_and_return_ptr(const std::string type = invert_options()[0],
                                     const Parameter mu = Parameter()) const
  {
    return new InverseType(invert(type, mu));
  }

  InverseType* invert_and_return_ptr(const Stuff::Common::Configuration& option,
                                     const Parameter mu = Parameter()) const
  {
    return new InverseType(invert(option, mu));
  }

  void apply_inverse(const RangeType& range,
                     SourceType& source,
                     const std::string type = invert_options()[0],
                     const Parameter mu = Parameter()) const
  {
    invert(type, mu).apply(range, source);
  }

  void apply_inverse(const RangeType& range,
                     SourceType& source,
                     const Stuff::Common::Configuration& option,
                     const Parameter mu = Parameter()) const
  {
    invert(option, mu).apply(range, source);
  }

  SourceType apply_inverse(const RangeType& range,
                           const std::string type = invert_options()[0],
                           const Parameter mu = Parameter()) const
  {
    SourceType source(dim_source());
    apply_inverse(range, source, type, mu);
    return source;
  }

  SourceType apply_inverse(const RangeType& range,
                           const Stuff::Common::Configuration& option,
                           const Parameter mu = Parameter()) const
  {
    SourceType source(dim_source());
    apply_inverse(range, source, option, mu);
    return source;
  }

  SourceType* apply_inverse_and_return_ptr(const RangeType& range,
                                           const std::string tyep = invert_options()[0],
                                           const Parameter mu = Parameter()) const
  {
    return new SourceType(apply_inverse(range, tyep, mu));
  }

  SourceType* apply_inverse_and_return_ptr(const RangeType& range,
                                           const Stuff::Common::Configuration& option,
                                           const Parameter mu = Parameter()) const
  {
    return new SourceType(apply_inverse(range, option, mu));
  }

  /**
   * \note  May throw Exceptions::this_is_not_parametric.
   */
  FrozenType freeze_parameter(const Parameter mu = Parameter()) const
  {
    CHECK_CRTP(this->as_imp(*this).freeze_parameter(mu));
    return this->as_imp(*this).freeze_parameter(mu);
  }

  FrozenType* freeze_parameter_and_return_ptr(const Parameter mu = Parameter()) const
  {
    return new FrozenType(freeze_parameter(mu));
  }
}; // class OperatorInterface

template< class Traits >
class AffinelyDecomposedOperatorInterface
  : public OperatorInterface< Traits >
  , public Tags::AffinelyDecomposedOperatorInterface
{
  typedef Pymor::OperatorInterface< Traits > BaseType;
public:
  typedef typename Traits::derived_type   derived_type;
  typedef typename Traits::ComponentType  ComponentType;
  static_assert(std::is_base_of< Pymor::OperatorInterface< typename ComponentType::Traits >, ComponentType >::value,
                "ComponentType has to be derived from OperatorInterface");

  AffinelyDecomposedOperatorInterface(const ParameterType mu = ParameterType())
    : BaseType(mu)
  {}

  AffinelyDecomposedOperatorInterface(const Parametric& other)
    : BaseType(other)
  {}

  DUNE_STUFF_SSIZE_T num_components() const
  {
    CHECK_CRTP(this->as_imp(*this).num_components());
    return this->as_imp(*this).num_components();
  }

  /**
   * \note  May throw one of Stuff::Exceptions::requirements_not_met, Stuff::Exceptions::index_out_of_range.
   */
  ComponentType component(const DUNE_STUFF_SSIZE_T qq) const
  {
    CHECK_CRTP(this->as_imp(*this).component(qq));
    return this->as_imp(*this).component(qq);
  }

  ComponentType* component_and_return_ptr(const DUNE_STUFF_SSIZE_T qq) const
  {
    return new ComponentType(component(qq));
  }

  /**
   * \note  May throw one of Stuff::Exceptions::requirements_not_met, Stuff::Exceptions::index_out_of_range.
   */
  ParameterFunctional coefficient(const DUNE_STUFF_SSIZE_T qq) const
  {
    CHECK_CRTP(this->as_imp(*this).coefficient(qq));
    return this->as_imp(*this).coefficient(qq);
  }

  ParameterFunctional* coefficient_and_return_ptr(const DUNE_STUFF_SSIZE_T qq) const
  {
    return new ParameterFunctional(coefficient(qq));
  }

  bool has_affine_part() const
  {
    CHECK_CRTP(this->as_imp(*this).has_affine_part());
    return this->as_imp(*this).has_affine_part();
  }

  /**
   * \note  May throw Stuff::Exceptions::requirements_not_met.
   */
  ComponentType affine_part() const
  {
    CHECK_CRTP(this->as_imp(*this).affine_part());
    return this->as_imp(*this).affine_part();
  }

  ComponentType* affine_part_and_return_ptr() const
  {
    return new ComponentType(affine_part());
  }
}; // class AffinelyDecomposedOperatorInterface

} // namespace Pymor
} // namespace Dune

#endif // DUNE_PYMOR_OPERATORS_INTERFACES_HH
<commit_msg>[operators.interfaces] remove useless scoped timings<commit_after>// This file is part of the dune-pymor project:
//   https://github.com/pymor/dune-pymor
// Copyright holders: Stephan Rave, Felix Schindler
// License: BSD 2-Clause License (http://opensource.org/licenses/BSD-2-Clause)

#ifndef DUNE_PYMOR_OPERATORS_INTERFACES_HH
#define DUNE_PYMOR_OPERATORS_INTERFACES_HH

#include <dune/stuff/common/configuration.hh>
#include <dune/stuff/common/crtp.hh>
#include <dune/stuff/common/profiler.hh>
#include <dune/stuff/common/type_utils.hh>
#include <dune/stuff/la/container/interfaces.hh>
#include <dune/stuff/la/solver.hh>

#include <dune/pymor/common/exceptions.hh>
#include <dune/pymor/parameters/base.hh>
#include <dune/pymor/parameters/functional.hh>

namespace Dune {
namespace Pymor {

/**
 *  \brief  Contains tags mostly needed for python bindings.
 */
namespace Tags {

class OperatorInterface {};

class AffinelyDecomposedOperatorInterface : public OperatorInterface {};

} // namespace Tags

template< class Traits >
class OperatorInterface
  : public Parametric
  , public Tags::OperatorInterface
  , public Stuff::CRTPInterface< OperatorInterface< Traits >, Traits >
{
public:
  typedef typename Traits::derived_type derived_type;
  typedef typename Traits::SourceType   SourceType;
  typedef typename Traits::RangeType    RangeType;
  typedef typename Traits::ScalarType   ScalarType;
  typedef typename Traits::FrozenType   FrozenType;
  typedef typename Traits::InverseType  InverseType;

  static_assert(std::is_base_of< Stuff::LA::VectorInterface< typename SourceType::Traits >, SourceType >::value,
                "SourceType has to be derived from Stuff::LA::VectorInterface!");
  static_assert(std::is_base_of< Stuff::LA::VectorInterface< typename RangeType::Traits >, RangeType >::value,
                "RangeType has to be derived from Stuff::LA::VectorInterface!");

  static std::string static_id() {    return "pymor.operators.interface"; }
  static std::string type_this() {    return Stuff::Common::Typename< derived_type >::value(); }
  static std::string type_source() {  return Stuff::Common::Typename< SourceType >::value(); }
  static std::string type_range() {   return Stuff::Common::Typename< RangeType >::value(); }
  static std::string type_scalar() {  return Stuff::Common::Typename< ScalarType >::value(); }
  static std::string type_frozen() {  return Stuff::Common::Typename< FrozenType >::value(); }
  static std::string type_inverse() { return Stuff::Common::Typename< InverseType >::value(); }

  OperatorInterface(const ParameterType mu = ParameterType())
    : Parametric(mu)
  {}

  OperatorInterface(const Parametric& other)
    : Parametric(other)
  {}

  bool linear() const
  {
    CHECK_CRTP(this->as_imp(*this).linear());
    return this->as_imp(*this).linear();
  }

  DUNE_STUFF_SSIZE_T dim_source() const
  {
    CHECK_CRTP(this->as_imp(*this).dim_source());
    return this->as_imp(*this).dim_source();
  }

  DUNE_STUFF_SSIZE_T dim_range() const
  {
    CHECK_CRTP(this->as_imp(*this).dim_range());
    return this->as_imp(*this).dim_range();
  }

  void apply(const SourceType& source, RangeType& range, const Parameter mu = Parameter()) const
  {
    CHECK_AND_CALL_CRTP(this->as_imp(*this).apply(source, range, mu));
  }

  RangeType apply(const SourceType& source, const Parameter mu = Parameter()) const
  {
    DUNE_STUFF_PROFILE_SCOPE(static_id() + ".apply");
    RangeType range(dim_range());
    apply(source, range, mu);
    return range;
  }

  RangeType* apply_and_return_ptr(const SourceType& source, const Parameter mu = Parameter()) const
  {
    return new RangeType(apply(source, mu));
  }

  /**
   * \note  This default implementation of apply2 creates a temporary vector. Any derived class which can do better
   *        should implement this method!
   */
  ScalarType apply2(const RangeType& range, const SourceType& source, const Parameter mu = Parameter()) const
  {
    RangeType tmp = range.copy();
    apply(source, tmp, mu);
    return tmp.dot(range);
  }

  static std::vector< std::string > invert_options()
  {
    return derived_type::invert_options();
  }

  static Stuff::Common::Configuration invert_options(const std::string& type)
  {
    return derived_type::invert_options(type);
  }

  InverseType invert(const std::string type = invert_options()[0], const Parameter mu = Parameter()) const
  {
    return invert(invert_options(type), mu);
  }

  InverseType invert(const Stuff::Common::Configuration& option, const Parameter mu = Parameter()) const
  {
    CHECK_CRTP(this->as_imp(*this).invert(option, mu));
    return this->as_imp(*this).invert(option, mu);
  }

  InverseType* invert_and_return_ptr(const std::string type = invert_options()[0],
                                     const Parameter mu = Parameter()) const
  {
    return new InverseType(invert(type, mu));
  }

  InverseType* invert_and_return_ptr(const Stuff::Common::Configuration& option,
                                     const Parameter mu = Parameter()) const
  {
    return new InverseType(invert(option, mu));
  }

  void apply_inverse(const RangeType& range,
                     SourceType& source,
                     const std::string type = invert_options()[0],
                     const Parameter mu = Parameter()) const
  {
    invert(type, mu).apply(range, source);
  }

  void apply_inverse(const RangeType& range,
                     SourceType& source,
                     const Stuff::Common::Configuration& option,
                     const Parameter mu = Parameter()) const
  {
    invert(option, mu).apply(range, source);
  }

  SourceType apply_inverse(const RangeType& range,
                           const std::string type = invert_options()[0],
                           const Parameter mu = Parameter()) const
  {
    SourceType source(dim_source());
    apply_inverse(range, source, type, mu);
    return source;
  }

  SourceType apply_inverse(const RangeType& range,
                           const Stuff::Common::Configuration& option,
                           const Parameter mu = Parameter()) const
  {
    SourceType source(dim_source());
    apply_inverse(range, source, option, mu);
    return source;
  }

  SourceType* apply_inverse_and_return_ptr(const RangeType& range,
                                           const std::string tyep = invert_options()[0],
                                           const Parameter mu = Parameter()) const
  {
    return new SourceType(apply_inverse(range, tyep, mu));
  }

  SourceType* apply_inverse_and_return_ptr(const RangeType& range,
                                           const Stuff::Common::Configuration& option,
                                           const Parameter mu = Parameter()) const
  {
    return new SourceType(apply_inverse(range, option, mu));
  }

  /**
   * \note  May throw Exceptions::this_is_not_parametric.
   */
  FrozenType freeze_parameter(const Parameter mu = Parameter()) const
  {
    CHECK_CRTP(this->as_imp(*this).freeze_parameter(mu));
    return this->as_imp(*this).freeze_parameter(mu);
  }

  FrozenType* freeze_parameter_and_return_ptr(const Parameter mu = Parameter()) const
  {
    return new FrozenType(freeze_parameter(mu));
  }
}; // class OperatorInterface

template< class Traits >
class AffinelyDecomposedOperatorInterface
  : public OperatorInterface< Traits >
  , public Tags::AffinelyDecomposedOperatorInterface
{
  typedef Pymor::OperatorInterface< Traits > BaseType;
public:
  typedef typename Traits::derived_type   derived_type;
  typedef typename Traits::ComponentType  ComponentType;
  static_assert(std::is_base_of< Pymor::OperatorInterface< typename ComponentType::Traits >, ComponentType >::value,
                "ComponentType has to be derived from OperatorInterface");

  AffinelyDecomposedOperatorInterface(const ParameterType mu = ParameterType())
    : BaseType(mu)
  {}

  AffinelyDecomposedOperatorInterface(const Parametric& other)
    : BaseType(other)
  {}

  DUNE_STUFF_SSIZE_T num_components() const
  {
    CHECK_CRTP(this->as_imp(*this).num_components());
    return this->as_imp(*this).num_components();
  }

  /**
   * \note  May throw one of Stuff::Exceptions::requirements_not_met, Stuff::Exceptions::index_out_of_range.
   */
  ComponentType component(const DUNE_STUFF_SSIZE_T qq) const
  {
    CHECK_CRTP(this->as_imp(*this).component(qq));
    return this->as_imp(*this).component(qq);
  }

  ComponentType* component_and_return_ptr(const DUNE_STUFF_SSIZE_T qq) const
  {
    return new ComponentType(component(qq));
  }

  /**
   * \note  May throw one of Stuff::Exceptions::requirements_not_met, Stuff::Exceptions::index_out_of_range.
   */
  ParameterFunctional coefficient(const DUNE_STUFF_SSIZE_T qq) const
  {
    CHECK_CRTP(this->as_imp(*this).coefficient(qq));
    return this->as_imp(*this).coefficient(qq);
  }

  ParameterFunctional* coefficient_and_return_ptr(const DUNE_STUFF_SSIZE_T qq) const
  {
    return new ParameterFunctional(coefficient(qq));
  }

  bool has_affine_part() const
  {
    CHECK_CRTP(this->as_imp(*this).has_affine_part());
    return this->as_imp(*this).has_affine_part();
  }

  /**
   * \note  May throw Stuff::Exceptions::requirements_not_met.
   */
  ComponentType affine_part() const
  {
    CHECK_CRTP(this->as_imp(*this).affine_part());
    return this->as_imp(*this).affine_part();
  }

  ComponentType* affine_part_and_return_ptr() const
  {
    return new ComponentType(affine_part());
  }
}; // class AffinelyDecomposedOperatorInterface

} // namespace Pymor
} // namespace Dune

#endif // DUNE_PYMOR_OPERATORS_INTERFACES_HH
<|endoftext|>


<commit_before>/*! \file
 *
 * \brief Conversion between python and C++ (header)
 * \author Benjamin Pritchard (ben@bennyp.org)
 */

#ifndef _GUARD_CONVERT_HPP_
#define _GUARD_CONVERT_HPP_

#include "bpmodule/python/Pybind11.hpp"
#include "bpmodule/python/Pybind11_stl.hpp"
#include "bpmodule/python/Types.hpp"
#include "bpmodule/python/Errors.hpp"
#include "bpmodule/exception/PythonConvertException.hpp"
#include "bpmodule/exception/Assert.hpp"
#include "bpmodule/util/Mangle.hpp"

namespace bpmodule {
namespace python {

/*! \brief Convert a python object to a C++ object
 *
 * \throw bpmodule::exception::PythonConvertException on error
 *
 * \tparam T The C++ type to convert to
 * \param [in] obj The python object to convert
 */
template<typename T>
T ConvertToCpp(pybind11::object obj)
{
    using bpmodule::exception::PythonConvertException;
    using bpmodule::exception::GeneralException;
    using bpmodule::exception::Assert;

    Assert<GeneralException>(obj.ptr() != nullptr, "Python object pointer is null");

    //! \todo more restrictive?
    try {
        return obj.cast<T>();
    }
    catch(const std::exception & ex)
    {
        throw PythonConvertException("Cannot convert from python to C++: Conversion failed",
                                     GetPyClass(obj), util::DemangleCppType<T>(),
                                     "what", ex.what());
    }
    catch(...)
    {
        throw PythonConvertException("Cannot convert from python to C++: Conversion failed",
                                     GetPyClass(obj), util::DemangleCppType<T>(),
                                     "what", "unknown exception type");
    }
}

/*! \brief Convert a C++ object to a python object
 *
 * \throw bpmodule::exception::PythonConvertException on error
 *
 * \tparam T The C++ type to convert from
 * \param [in] obj The python object to convert
 */
template<typename T>
pybind11::object ConvertToPy(const T & obj,
                             pybind11::return_value_policy pol = pybind11::return_value_policy::copy)
{
    using bpmodule::exception::PythonConvertException;

    // may NOT throw if there is an issue
    try {
        pybind11::object pyobj = pybind11::cast(obj, pol);

        //! \todo fix if this pybind11 is changed to throw an exception
        if(!pyobj)
        {
            throw PythonConvertException(detail::GetPyException(),
                                         util::DemangleCppType<T>(), "python object",
                                         "info", "Resulting object pointer is null");
        }
        else
            return pyobj;

    }
    catch (const std::exception & ex)
    {
        throw PythonConvertException(ex.what(),
                                     util::DemangleCppType<T>(), "python object",
                                     "info", "In converting from C++ to python object");
    }
    catch(...)
    {
        throw PythonConvertException("Caught unknown exception",
                                     util::DemangleCppType<T>(), "python object",
                                     "info", "In converting from C++ to python object");
    }
}

/*! \brief Convert a plain array of C++ objects to a python object
 *
 * \throw exception::PythonConvertException if the
 *        data could not be converted
 *
 * \tparam T The C++ type to convert from
 *
 * \param [in] obj An array of objects
 * \param [in] n Length of the array
 * \return Converted data as a python object
 */
template<typename T>
pybind11::object ConvertToPy(const T * const obj, size_t n)
{
    std::vector<T> v(obj, obj+n);

    return ConvertToPy(v);
}

} // close namespace python
} // close namespace bpmodule

#endif
<commit_msg>Add some restrictions<commit_after>/*! \file
 *
 * \brief Conversion between python and C++ (header)
 * \author Benjamin Pritchard (ben@bennyp.org)
 */

#ifndef _GUARD_CONVERT_HPP_
#define _GUARD_CONVERT_HPP_

#include "bpmodule/python/Pybind11.hpp"
#include "bpmodule/python/Pybind11_stl.hpp"
#include "bpmodule/python/Types.hpp"
#include "bpmodule/python/Errors.hpp"
#include "bpmodule/exception/PythonConvertException.hpp"
#include "bpmodule/exception/Assert.hpp"
#include "bpmodule/util/Mangle.hpp"

namespace bpmodule {
namespace python {

/*! \brief Prevent some conversions
 */
template<typename T>
bool ValidConvert(pybind11::object obj)
{
    //! \todo Won't catch elements in lists, etc. Would have to override in pybind11?
    std::string cls = GetPyClass(obj);
    if(std::is_integral<T>::value && cls != "int")
        return false;
    if(std::is_floating_point<T>::value && cls != "float")
        return false;
    if(std::is_same<T, bool>::value && cls != "bool")
        return false;

    return true;
}

/*! \brief Convert a python object to a C++ object
 *
 * \throw bpmodule::exception::PythonConvertException on error
 *
 * \tparam T The C++ type to convert to
 * \param [in] obj The python object to convert
 */
template<typename T>
T ConvertToCpp(pybind11::object obj)
{
    using bpmodule::exception::PythonConvertException;
    using bpmodule::exception::GeneralException;
    using bpmodule::exception::Assert;

    Assert<GeneralException>(obj.ptr() != nullptr, "Python object pointer is null");

    if(!ValidConvert<T>(obj))
        throw PythonConvertException("Cannot convert from python to C++: Incompatible types",
                                     GetPyClass(obj), util::DemangleCppType<T>());

    try {
        return obj.cast<T>();
    }
    catch(const std::exception & ex)
    {
        throw PythonConvertException("Cannot convert from python to C++: Conversion failed",
                                     GetPyClass(obj), util::DemangleCppType<T>(),
                                     "what", ex.what());
    }
    catch(...)
    {
        throw PythonConvertException("Cannot convert from python to C++: Conversion failed",
                                     GetPyClass(obj), util::DemangleCppType<T>(),
                                     "what", "unknown exception type");
    }
}

/*! \brief Convert a C++ object to a python object
 *
 * \throw bpmodule::exception::PythonConvertException on error
 *
 * \tparam T The C++ type to convert from
 * \param [in] obj The python object to convert
 */
template<typename T>
pybind11::object ConvertToPy(const T & obj,
                             pybind11::return_value_policy pol = pybind11::return_value_policy::copy)
{
    using bpmodule::exception::PythonConvertException;

    // may NOT throw if there is an issue
    try {
        pybind11::object pyobj = pybind11::cast(obj, pol);

        //! \todo fix if this pybind11 is changed to throw an exception
        if(!pyobj)
        {
            throw PythonConvertException(detail::GetPyException(),
                                         util::DemangleCppType<T>(), "python object",
                                         "info", "Resulting object pointer is null");
        }
        else
            return pyobj;

    }
    catch (const std::exception & ex)
    {
        throw PythonConvertException(ex.what(),
                                     util::DemangleCppType<T>(), "python object",
                                     "info", "In converting from C++ to python object");
    }
    catch(...)
    {
        throw PythonConvertException("Caught unknown exception",
                                     util::DemangleCppType<T>(), "python object",
                                     "info", "In converting from C++ to python object");
    }
}

/*! \brief Convert a plain array of C++ objects to a python object
 *
 * \throw exception::PythonConvertException if the
 *        data could not be converted
 *
 * \tparam T The C++ type to convert from
 *
 * \param [in] obj An array of objects
 * \param [in] n Length of the array
 * \return Converted data as a python object
 */
template<typename T>
pybind11::object ConvertToPy(const T * const obj, size_t n)
{
    std::vector<T> v(obj, obj+n);

    return ConvertToPy(v);
}

} // close namespace python
} // close namespace bpmodule

#endif
<|endoftext|>