<commit_before>#include "config.h"
#include "ylib.h"
#include "ylocale.h"
#include "yapp.h"
#include "yxtray.h"

extern void logEvent(const XEvent &xev);

char const *ApplicationName = "icewmtray";

YColor *taskBarBg;

class SysTray: public YWindow, public YXTrayNotifier {
public:
    SysTray();
    bool checkMessageEvent(const XClientMessageEvent &message);
    void requestDock();

    void handleUnmap(const XUnmapEvent &) {
        puts("hidexxx");
        if (visible())
            hide();
    }
    void trayChanged();
private:
    Atom icewm_internal_tray;
    Atom _NET_SYSTEM_TRAY_OPCODE;
    YXTray *fTray2;
};

class SysTrayApp: public YApplication {
public:
    SysTrayApp(int *argc, char ***argv, const char *displayName = 0);
    ~SysTrayApp();

    bool filterEvent(const XEvent &xev);
private:
    SysTray *tray;
};

SysTrayApp::SysTrayApp(int *argc, char ***argv, const char *displayName):
    YApplication(argc, argv, displayName)
{
    desktop->setStyle(YWindow::wsDesktopAware);
    tray = new SysTray();
}

SysTrayApp::~SysTrayApp() {

}

bool SysTrayApp::filterEvent(const XEvent &xev) {
#ifdef DEBUG
    logEvent(xev);
#endif
    if (xev.type == ClientMessage) {
        tray->checkMessageEvent(xev.xclient);
        return false;
    } else if (xev.type == MappingNotify) {
            puts("tray mapping1");
        if (xev.xmapping.window == tray->handle()) {
            puts("tray mapping");
        }
    }
    return false;
}

SysTray::SysTray(): YWindow(0) {
    desktop->setStyle(YWindow::wsDesktopAware);
    fTray2 = new YXTray(this, "_NET_SYSTEM_TRAY_S0", this);

    icewm_internal_tray =
        XInternAtom(app->display(), "_ICEWM_INTTRAY", False);

    _NET_SYSTEM_TRAY_OPCODE =
        XInternAtom(app->display(),
                    "_NET_SYSTEM_TRAY_OPCODE",
                    False);

    fTray2->relayout();
    setSize(fTray2->width(),
            fTray2->height());
    fTray2->show();
    requestDock();
}

void SysTray::trayChanged() {
    fTray2->relayout();
    setSize(fTray2->width(),
            fTray2->height());
}

void SysTray::requestDock() {
    Window w = XGetSelectionOwner(app->display(), icewm_internal_tray);

    if (w && w != handle()) {
        XClientMessageEvent xev;
        memset(&xev, 0, sizeof(xev));

        xev.type = ClientMessage;
        xev.window = w;
        xev.message_type = _NET_SYSTEM_TRAY_OPCODE;
        xev.format = 32;
        xev.data.l[0] = CurrentTime;
        xev.data.l[1] = SYSTEM_TRAY_REQUEST_DOCK;
        xev.data.l[2] = handle(); //fTray2->handle();

        XSendEvent(app->display(), w, False, StructureNotifyMask, (XEvent *) &xev);
    }
}

bool SysTray::checkMessageEvent(const XClientMessageEvent &message) {
    if (message.message_type == icewm_internal_tray) {
        puts("requestDock");
        requestDock();
    }
    return true;
}

int main(int argc, char **argv) {
    YLocale locale;

    SysTrayApp app(&argc, &argv);

    taskBarBg = new YColor("#C0C0C0"); /// FIXME

    return app.mainLoop();
}
<commit_msg>build fix<commit_after>#include "config.h"
#include "ylib.h"
#include "ylocale.h"
#include "yapp.h"
#include "yxtray.h"
#include "base.h"
#include "stdlib.h"

extern void logEvent(const XEvent &xev);

char const *ApplicationName = "icewmtray";

YColor *taskBarBg;

class SysTray: public YWindow, public YXTrayNotifier {
public:
    SysTray();
    bool checkMessageEvent(const XClientMessageEvent &message);
    void requestDock();

    void handleUnmap(const XUnmapEvent &) {
        msg("hide");
        if (visible())
            hide();
    }
    void trayChanged();
private:
    Atom icewm_internal_tray;
    Atom _NET_SYSTEM_TRAY_OPCODE;
    YXTray *fTray2;
};

class SysTrayApp: public YApplication {
public:
    SysTrayApp(int *argc, char ***argv, const char *displayName = 0);
    ~SysTrayApp();

    bool filterEvent(const XEvent &xev);
private:
    SysTray *tray;
};

SysTrayApp::SysTrayApp(int *argc, char ***argv, const char *displayName):
    YApplication(argc, argv, displayName)
{
    desktop->setStyle(YWindow::wsDesktopAware);
    tray = new SysTray();
}

SysTrayApp::~SysTrayApp() {

}

bool SysTrayApp::filterEvent(const XEvent &xev) {
#ifdef DEBUG
    logEvent(xev);
#endif
    if (xev.type == ClientMessage) {
        tray->checkMessageEvent(xev.xclient);
        return false;
    } else if (xev.type == MappingNotify) {
            puts("tray mapping1");
        if (xev.xmapping.window == tray->handle()) {
            puts("tray mapping");
        }
    }
    return false;
}

SysTray::SysTray(): YWindow(0) {
    desktop->setStyle(YWindow::wsDesktopAware);
    fTray2 = new YXTray(this, "_NET_SYSTEM_TRAY_S0", this);

    icewm_internal_tray =
        XInternAtom(app->display(), "_ICEWM_INTTRAY", False);

    _NET_SYSTEM_TRAY_OPCODE =
        XInternAtom(app->display(),
                    "_NET_SYSTEM_TRAY_OPCODE",
                    False);

    fTray2->relayout();
    setSize(fTray2->width(),
            fTray2->height());
    fTray2->show();
    requestDock();
}

void SysTray::trayChanged() {
    fTray2->relayout();
    setSize(fTray2->width(),
            fTray2->height());
}

void SysTray::requestDock() {
    Window w = XGetSelectionOwner(app->display(), icewm_internal_tray);

    if (w && w != handle()) {
        XClientMessageEvent xev;
        memset(&xev, 0, sizeof(xev));

        xev.type = ClientMessage;
        xev.window = w;
        xev.message_type = _NET_SYSTEM_TRAY_OPCODE;
        xev.format = 32;
        xev.data.l[0] = CurrentTime;
        xev.data.l[1] = SYSTEM_TRAY_REQUEST_DOCK;
        xev.data.l[2] = handle(); //fTray2->handle();

        XSendEvent(app->display(), w, False, StructureNotifyMask, (XEvent *) &xev);
    }
}

bool SysTray::checkMessageEvent(const XClientMessageEvent &message) {
    if (message.message_type == icewm_internal_tray) {
        puts("requestDock");
        requestDock();
    }
    return true;
}

int main(int argc, char **argv) {
    YLocale locale;

    SysTrayApp app(&argc, &argv);

    taskBarBg = new YColor("#C0C0C0"); /// FIXME

    return app.mainLoop();
}
<|endoftext|>


<commit_before>/*************************************************************************
 *
 *  OpenOffice.org - a multi-platform office productivity suite
 *
 *  $RCSfile: servicenames.hxx,v $
 *
 *  $Revision: 1.2 $
 *
 *  last change: $Author: rt $ $Date: 2005-09-08 00:47:49 $
 *
 *  The Contents of this file are made available subject to
 *  the terms of GNU Lesser General Public License Version 2.1.
 *
 *
 *    GNU Lesser General Public License Version 2.1
 *    =============================================
 *    Copyright 2005 by Sun Microsystems, Inc.
 *    901 San Antonio Road, Palo Alto, CA 94303, USA
 *
 *    This library is free software; you can redistribute it and/or
 *    modify it under the terms of the GNU Lesser General Public
 *    License version 2.1, as published by the Free Software Foundation.
 *
 *    This library is distributed in the hope that it will be useful,
 *    but WITHOUT ANY WARRANTY; without even the implied warranty of
 *    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 *    Lesser General Public License for more details.
 *
 *    You should have received a copy of the GNU Lesser General Public
 *    License along with this library; if not, write to the Free Software
 *    Foundation, Inc., 59 Temple Place, Suite 330, Boston,
 *    MA  02111-1307  USA
 *
 ************************************************************************/
#ifndef _CHART_SERVICENAMES_HXX
#define _CHART_SERVICENAMES_HXX

//.............................................................................
namespace chart
{
//.............................................................................

#define CHART_MODEL_SERVICE_IMPLEMENTATION_NAME ::rtl::OUString::createFromAscii("com.sun.star.comp.chart2.ChartModel")
#define CHART_MODEL_SERVICE_NAME ::rtl::OUString::createFromAscii("com.sun.star.chart2.ChartModel")
//@todo create your own service containing the service com.sun.star.document.OfficeDocument

#define CHART_CONTROLLER_SERVICE_IMPLEMENTATION_NAME ::rtl::OUString::createFromAscii("com.sun.star.comp.chart2.ChartController")
#define CHART_CONTROLLER_SERVICE_NAME ::rtl::OUString::createFromAscii("com.sun.star.chart2.ChartController")
//@todo create your own service containing the service com.sun.star.frame.Controller

#define CHART_FRAMELOADER_SERVICE_IMPLEMENTATION_NAME ::rtl::OUString::createFromAscii("com.sun.star.comp.chart2.ChartFrameLoader")
#define CHART_FRAMELOADER_SERVICE_NAME ::rtl::OUString::createFromAscii("com.sun.star.frame.SynchronousFrameLoader")

//.............................................................................
} //namespace chart
//.............................................................................

#endif
<commit_msg>INTEGRATION: CWS chart2mst3 (1.1.1.1.4); FILE MERGED 2006/05/11 08:55:12 bm 1.1.1.1.4.7: the ChartModel service name is ChartDocument 2006/03/23 09:58:04 bm 1.1.1.1.4.6: helper component for text accessibility 2005/10/07 11:47:32 bm 1.1.1.1.4.5: RESYNC: (1.1.1.1-1.2); FILE MERGED 2005/07/08 14:58:33 iha 1.1.1.1.4.4: create and notify an accessible view 2005/05/31 18:31:36 iha 1.1.1.1.4.3: added service names for chart view 2004/04/07 21:47:44 iha 1.1.1.1.4.2: #i20344# added WizardDialog 2004/02/13 16:51:25 bm 1.1.1.1.4.1: join from changes on branch bm_post_chart01<commit_after>/*************************************************************************
 *
 *  OpenOffice.org - a multi-platform office productivity suite
 *
 *  $RCSfile: servicenames.hxx,v $
 *
 *  $Revision: 1.3 $
 *
 *  last change: $Author: vg $ $Date: 2007-05-22 18:25:41 $
 *
 *  The Contents of this file are made available subject to
 *  the terms of GNU Lesser General Public License Version 2.1.
 *
 *
 *    GNU Lesser General Public License Version 2.1
 *    =============================================
 *    Copyright 2005 by Sun Microsystems, Inc.
 *    901 San Antonio Road, Palo Alto, CA 94303, USA
 *
 *    This library is free software; you can redistribute it and/or
 *    modify it under the terms of the GNU Lesser General Public
 *    License version 2.1, as published by the Free Software Foundation.
 *
 *    This library is distributed in the hope that it will be useful,
 *    but WITHOUT ANY WARRANTY; without even the implied warranty of
 *    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 *    Lesser General Public License for more details.
 *
 *    You should have received a copy of the GNU Lesser General Public
 *    License along with this library; if not, write to the Free Software
 *    Foundation, Inc., 59 Temple Place, Suite 330, Boston,
 *    MA  02111-1307  USA
 *
 ************************************************************************/
#ifndef _CHART_SERVICENAMES_HXX
#define _CHART_SERVICENAMES_HXX

//.............................................................................
namespace chart
{
//.............................................................................

#define CHART_MODEL_SERVICE_IMPLEMENTATION_NAME ::rtl::OUString::createFromAscii("com.sun.star.comp.chart2.ChartModel")
#define CHART_MODEL_SERVICE_NAME ::rtl::OUString::createFromAscii("com.sun.star.chart2.ChartDocument")
//@todo create your own service containing the service com.sun.star.document.OfficeDocument

#define CHART_CONTROLLER_SERVICE_IMPLEMENTATION_NAME ::rtl::OUString::createFromAscii("com.sun.star.comp.chart2.ChartController")
#define CHART_CONTROLLER_SERVICE_NAME ::rtl::OUString::createFromAscii("com.sun.star.chart2.ChartController")
//@todo create your own service containing the service com.sun.star.frame.Controller

#define CHART_VIEW_SERVICE_IMPLEMENTATION_NAME ::rtl::OUString::createFromAscii("com.sun.star.comp.chart2.ChartView")
#define CHART_VIEW_SERVICE_NAME ::rtl::OUString::createFromAscii("com.sun.star.chart2.ChartView")

#define CHART_FRAMELOADER_SERVICE_IMPLEMENTATION_NAME ::rtl::OUString::createFromAscii("com.sun.star.comp.chart2.ChartFrameLoader")
#define CHART_FRAMELOADER_SERVICE_NAME ::rtl::OUString::createFromAscii("com.sun.star.frame.SynchronousFrameLoader")

#define CHART_WIZARD_DIALOG_SERVICE_IMPLEMENTATION_NAME ::rtl::OUString::createFromAscii("com.sun.star.comp.chart2.WizardDialog")
#define CHART_WIZARD_DIALOG_SERVICE_NAME ::rtl::OUString::createFromAscii("com.sun.star.chart2.WizardDialog")

// wrapper for old UNO API (com.sun.star.chart)
#define CHART_CHARTAPIWRAPPER_IMPLEMENTATION_NAME ::rtl::OUString::createFromAscii("com.sun.star.comp.chart2.ChartDocumentWrapper")
#define CHART_CHARTAPIWRAPPER_SERVICE_NAME ::rtl::OUString::createFromAscii("com.sun.star.chart2.ChartDocumentWrapper")

// accessibility
#define CHART2_ACCESSIBLE_SERVICE_IMPLEMENTATION_NAME ::rtl::OUString::createFromAscii("com.sun.star.comp.chart2.AccessibleChartView")
#define CHART2_ACCESSIBLE_SERVICE_NAME ::rtl::OUString::createFromAscii("com.sun.star.chart2.AccessibleChartView")

#define CHART_ACCESSIBLE_TEXT_IMPLEMENTATION_NAME ::rtl::OUString::createFromAscii("com.sun.star.comp.chart2.AccessibleTextComponent")
#define CHART_ACCESSIBLE_TEXT_SERVICE_NAME ::rtl::OUString::createFromAscii("com.sun.star.accessibility.AccessibleTextComponent")
//.............................................................................
} //namespace chart
//.............................................................................

#endif
<|endoftext|>


<commit_before>/*
 * Copyright 2007-2017 Content Management AG
 * All rights reserved.
 *
 * author: Max Kellermann <mk@cm4all.com>
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions
 * are met:
 *
 * - Redistributions of source code must retain the above copyright
 * notice, this list of conditions and the following disclaimer.
 *
 * - Redistributions in binary form must reproduce the above copyright
 * notice, this list of conditions and the following disclaimer in the
 * documentation and/or other materials provided with the
 * distribution.
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
 * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
 * FOR A PARTICULAR PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE
 * FOUNDATION OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
 * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
 * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
 * OF THE POSSIBILITY OF SUCH DAMAGE.
 */

#include "Resolver.hxx"
#include "Registry.hxx"
#include "Widget.hxx"
#include "Class.hxx"
#include "pool/pool.hxx"
#include "util/Cancellable.hxx"

#include <boost/intrusive/list.hpp>

struct WidgetResolver;

struct WidgetResolverListener final
    : public boost::intrusive::list_base_hook<boost::intrusive::link_mode<boost::intrusive::normal_link>>,
      Cancellable {

    struct pool &pool;

    WidgetResolver &resolver;

    const WidgetResolverCallback callback;

#ifndef NDEBUG
    bool finished = false, aborted = false;
#endif

    WidgetResolverListener(struct pool &_pool, WidgetResolver &_resolver,
                           WidgetResolverCallback _callback,
                           CancellablePointer &cancel_ptr)
        :pool(_pool), resolver(_resolver),
         callback(_callback) {
        cancel_ptr = *this;
    }

    void Finish();

    /* virtual methods from class Cancellable */
    void Cancel() noexcept override;
};

struct WidgetResolver {
    Widget &widget;

    boost::intrusive::list<WidgetResolverListener,
                           boost::intrusive::constant_time_size<false>> listeners;

    CancellablePointer cancel_ptr;

    bool finished = false;

#ifndef NDEBUG
    bool running = false;
    bool aborted = false;
#endif

    explicit WidgetResolver(Widget &_widget)
        :widget(_widget) {}

    void Start(struct tcache &translate_cache) {
        /* use the widget pool because the listener pool may be
           aborted, while the others still run */
        widget_class_lookup(widget.pool, widget.pool, translate_cache,
                            widget.class_name,
                            BIND_THIS_METHOD(RegistryCallback),
                            cancel_ptr);
    }

    void RemoveListener(WidgetResolverListener &listener);
    void Abort();

    void RegistryCallback(const WidgetClass *cls);
};

void
WidgetResolver::RemoveListener(WidgetResolverListener &listener)
{
    listeners.erase(listeners.iterator_to(listener));

    if (listeners.empty() && !finished)
        /* the last listener has been aborted: abort the widget
           registry */
        Abort();
}

void
WidgetResolver::Abort()
{
    assert(listeners.empty());
    assert(widget.resolver == this);

#ifndef NDEBUG
    aborted = true;
#endif

    widget.resolver = nullptr;
    cancel_ptr.Cancel();
    pool_unref(&widget.pool);
}

/*
 * async operation
 *
 */

void
WidgetResolverListener::Cancel() noexcept
{
    assert(!finished);
    assert(!aborted);
    assert(resolver.widget.resolver == &resolver);
    assert(!resolver.listeners.empty());
    assert(!resolver.finished || resolver.running);
    assert(!resolver.aborted);

#ifndef NDEBUG
    aborted = true;
#endif

    resolver.RemoveListener(*this);

    pool_unref(&pool);
}

/*
 * registry callback
 *
 */

inline void
WidgetResolverListener::Finish()
{
    assert(!finished);
    assert(!aborted);

#ifndef NDEBUG
    finished = true;
#endif

    callback();
    pool_unref(&pool);
}

void
WidgetResolver::RegistryCallback(const WidgetClass *cls)
{
    assert(widget.cls == nullptr);
    assert(widget.resolver == this);
    assert(!listeners.empty());
    assert(!finished);
    assert(!running);
    assert(!aborted);

    finished = true;

#ifndef NDEBUG
    running = true;
#endif

    widget.cls = cls;

    widget.from_template.view = widget.from_request.view = cls != nullptr
        ? widget_view_lookup(&cls->views, widget.from_template.view_name)
        : nullptr;

    widget.session_sync_pending = cls != nullptr && cls->stateful &&
        /* the widget session code requires a valid view */
        widget.from_template.view != nullptr;

    do {
        auto &l = listeners.front();
        listeners.pop_front();
        l.Finish();
    } while (!listeners.empty());

#ifndef NDEBUG
    running = false;
#endif

    pool_unref(&widget.pool);
}

/*
 * constructor
 *
 */

static WidgetResolver *
widget_resolver_alloc(Widget &widget)
{
    auto &pool = widget.pool;

    pool_ref(&pool);

    return widget.resolver = NewFromPool<WidgetResolver>(pool, widget);
}

void
ResolveWidget(struct pool &pool,
              Widget &widget,
              struct tcache &translate_cache,
              WidgetResolverCallback callback,
              CancellablePointer &cancel_ptr)
{
    bool is_new = false;

    assert(widget.class_name != nullptr);
    assert(pool_contains(widget.pool, &widget, sizeof(widget)));

    if (widget.cls != nullptr) {
        /* already resolved successfully */
        callback();
        return;
    }

    /* create new resolver object if it does not already exist */

    WidgetResolver *resolver = widget.resolver;
    if (resolver == nullptr) {
        resolver = widget_resolver_alloc(widget);
        is_new = true;
    } else if (resolver->finished) {
        /* we have already failed to resolve this widget class; return
           immediately, don't try again */
        callback();
        return;
    }

    assert(pool_contains(widget.pool, widget.resolver,
                         sizeof(*widget.resolver)));

    /* add a new listener to the resolver */

    pool_ref(&pool);
    auto listener = NewFromPool<WidgetResolverListener>(pool, pool, *resolver,
                                                        callback,
                                                        cancel_ptr);

    resolver->listeners.push_back(*listener);

    /* finally send request to the widget registry */

    if (is_new)
        resolver->Start(translate_cache);
}
<commit_msg>widget/Resolver: move code to Destroy()<commit_after>/*
 * Copyright 2007-2017 Content Management AG
 * All rights reserved.
 *
 * author: Max Kellermann <mk@cm4all.com>
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions
 * are met:
 *
 * - Redistributions of source code must retain the above copyright
 * notice, this list of conditions and the following disclaimer.
 *
 * - Redistributions in binary form must reproduce the above copyright
 * notice, this list of conditions and the following disclaimer in the
 * documentation and/or other materials provided with the
 * distribution.
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
 * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
 * FOR A PARTICULAR PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE
 * FOUNDATION OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
 * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
 * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
 * OF THE POSSIBILITY OF SUCH DAMAGE.
 */

#include "Resolver.hxx"
#include "Registry.hxx"
#include "Widget.hxx"
#include "Class.hxx"
#include "pool/pool.hxx"
#include "util/Cancellable.hxx"

#include <boost/intrusive/list.hpp>

struct WidgetResolver;

struct WidgetResolverListener final
    : public boost::intrusive::list_base_hook<boost::intrusive::link_mode<boost::intrusive::normal_link>>,
      Cancellable {

    struct pool &pool;

    WidgetResolver &resolver;

    const WidgetResolverCallback callback;

#ifndef NDEBUG
    bool finished = false, aborted = false;
#endif

    WidgetResolverListener(struct pool &_pool, WidgetResolver &_resolver,
                           WidgetResolverCallback _callback,
                           CancellablePointer &cancel_ptr)
        :pool(_pool), resolver(_resolver),
         callback(_callback) {
        cancel_ptr = *this;
    }

    void Destroy() noexcept {
        pool_unref(&pool);
    }

    void Finish();

    /* virtual methods from class Cancellable */
    void Cancel() noexcept override;
};

struct WidgetResolver {
    Widget &widget;

    boost::intrusive::list<WidgetResolverListener,
                           boost::intrusive::constant_time_size<false>> listeners;

    CancellablePointer cancel_ptr;

    bool finished = false;

#ifndef NDEBUG
    bool running = false;
    bool aborted = false;
#endif

    explicit WidgetResolver(Widget &_widget)
        :widget(_widget) {}

    void Destroy() noexcept {
        pool_unref(&widget.pool);
    }

    void Start(struct tcache &translate_cache) {
        /* use the widget pool because the listener pool may be
           aborted, while the others still run */
        widget_class_lookup(widget.pool, widget.pool, translate_cache,
                            widget.class_name,
                            BIND_THIS_METHOD(RegistryCallback),
                            cancel_ptr);
    }

    void RemoveListener(WidgetResolverListener &listener);
    void Abort();

    void RegistryCallback(const WidgetClass *cls);
};

void
WidgetResolver::RemoveListener(WidgetResolverListener &listener)
{
    listeners.erase(listeners.iterator_to(listener));

    if (listeners.empty() && !finished)
        /* the last listener has been aborted: abort the widget
           registry */
        Abort();
}

void
WidgetResolver::Abort()
{
    assert(listeners.empty());
    assert(widget.resolver == this);

#ifndef NDEBUG
    aborted = true;
#endif

    widget.resolver = nullptr;
    cancel_ptr.Cancel();
    Destroy();
}

/*
 * async operation
 *
 */

void
WidgetResolverListener::Cancel() noexcept
{
    assert(!finished);
    assert(!aborted);
    assert(resolver.widget.resolver == &resolver);
    assert(!resolver.listeners.empty());
    assert(!resolver.finished || resolver.running);
    assert(!resolver.aborted);

#ifndef NDEBUG
    aborted = true;
#endif

    resolver.RemoveListener(*this);

    Destroy();
}

/*
 * registry callback
 *
 */

inline void
WidgetResolverListener::Finish()
{
    assert(!finished);
    assert(!aborted);

#ifndef NDEBUG
    finished = true;
#endif

    callback();
    Destroy();
}

void
WidgetResolver::RegistryCallback(const WidgetClass *cls)
{
    assert(widget.cls == nullptr);
    assert(widget.resolver == this);
    assert(!listeners.empty());
    assert(!finished);
    assert(!running);
    assert(!aborted);

    finished = true;

#ifndef NDEBUG
    running = true;
#endif

    widget.cls = cls;

    widget.from_template.view = widget.from_request.view = cls != nullptr
        ? widget_view_lookup(&cls->views, widget.from_template.view_name)
        : nullptr;

    widget.session_sync_pending = cls != nullptr && cls->stateful &&
        /* the widget session code requires a valid view */
        widget.from_template.view != nullptr;

    do {
        auto &l = listeners.front();
        listeners.pop_front();
        l.Finish();
    } while (!listeners.empty());

#ifndef NDEBUG
    running = false;
#endif

    Destroy();
}

/*
 * constructor
 *
 */

static WidgetResolver *
widget_resolver_alloc(Widget &widget)
{
    auto &pool = widget.pool;

    pool_ref(&pool);

    return widget.resolver = NewFromPool<WidgetResolver>(pool, widget);
}

void
ResolveWidget(struct pool &pool,
              Widget &widget,
              struct tcache &translate_cache,
              WidgetResolverCallback callback,
              CancellablePointer &cancel_ptr)
{
    bool is_new = false;

    assert(widget.class_name != nullptr);
    assert(pool_contains(widget.pool, &widget, sizeof(widget)));

    if (widget.cls != nullptr) {
        /* already resolved successfully */
        callback();
        return;
    }

    /* create new resolver object if it does not already exist */

    WidgetResolver *resolver = widget.resolver;
    if (resolver == nullptr) {
        resolver = widget_resolver_alloc(widget);
        is_new = true;
    } else if (resolver->finished) {
        /* we have already failed to resolve this widget class; return
           immediately, don't try again */
        callback();
        return;
    }

    assert(pool_contains(widget.pool, widget.resolver,
                         sizeof(*widget.resolver)));

    /* add a new listener to the resolver */

    pool_ref(&pool);
    auto listener = NewFromPool<WidgetResolverListener>(pool, pool, *resolver,
                                                        callback,
                                                        cancel_ptr);

    resolver->listeners.push_back(*listener);

    /* finally send request to the widget registry */

    if (is_new)
        resolver->Start(translate_cache);
}
<|endoftext|>


<commit_before>/*
 * Copyright (c) 2013 Hideaki Ohno <hide.o.j55{at}gmail.com>
 *
 * 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.
 */

#if !defined(HYPERLOGLOG_HPP)
#define HYPERLOGLOG_HPP

/**
 * @file hyperloglog.hpp
 * @brief HyperLogLog cardinality estimator
 * @date Created 2013/3/20
 * @author Hideaki Ohno
 */

#include <vector>
#include <cmath>
#include <sstream>
#include <stdexcept>
#include <algorithm>
#include "murmur3.h"

#define HLL_HASH_SEED 313

namespace hll {

static const double pow_2_32 = 4294967296.0; ///< 2^32
static const double neg_pow_2_32 = -4294967296.0; ///< -(2^32)

/** @class HyperLogLog
 *  @brief Implement of 'HyperLogLog' estimate cardinality algorithm
 */
class HyperLogLog {
public:

    /**
     * Constructor
     *
     * @param[in] b bit width (register size will be 2 to the b power).
     *            This value must be in the range[4,16].Default value is 4.
     *
     * @exception std::invalid_argument the argument is out of range.
     */
    HyperLogLog(uint8_t b = 4) throw (std::invalid_argument) :
            b_(b), m_(1 << b), M_(m_, 0) {

        if (b < 4 || 16 < b) {
            throw std::invalid_argument("bit width must be in the range [4,16]");
        }

        double alpha;
        switch (m_) {
            case 16:
                alpha = 0.673;
                break;
            case 32:
                alpha = 0.697;
                break;
            case 64:
                alpha = 0.709;
                break;
            default:
                alpha = 0.7213 / (1.0 + 1.079 / m_);
                break;
        }
        alphaMM_ = alpha * m_ * m_;
    }

    /**
     * Adds element to the estimator
     *
     * @param[in] str string to add
     * @param[in] len length of string
     */
    void add(const char* str, uint32_t len) {
        uint32_t hash;
        MurmurHash3_x86_32(str, len, HLL_HASH_SEED, (void*) &hash);
        uint32_t index = hash >> (32 - b_);
        uint8_t rank = rho((hash << b_), 32 - b_);
        if (rank > M_[index]) {
            M_[index] = rank;
        }
    }

    /**
     * Estimates cardinality value.
     *
     * @return Estimated cardinality value.
     */
    double estimate() const {
        double estimate;
        double sum = 0.0;
        for (uint32_t i = 0; i < m_; i++) {
            sum += 1.0 / pow(2.0, M_[i]);
        }
        estimate = alphaMM_ / sum; // E in the original paper
        if (estimate <= 2.5 * m_) {
            uint32_t zeros = 0;
            for (uint32_t i = 0; i < m_; i++) {
                if (M_[i] == 0) {
                    zeros++;
                }
            }
            if (zeros != 0) {
                estimate = m_ * log(static_cast<double>(m_)/ zeros);
            }
        } else if (estimate > (1.0 / 30.0) * pow_2_32) {
            estimate = neg_pow_2_32 * log(1.0 - (estimate / pow_2_32));
        }
        return estimate;
    }

    /**
     * Merges the estimate from 'other' into this object, returning the estimate of their union.
     * The number of registers in each must be the same.
     *
     * @param[in] other HyperLogLog instance to be merged
     *
     * @exception std::invalid_argument number of registers doesn't match.
     */
    void merge(const HyperLogLog& other) throw (std::invalid_argument) {
        if (m_ != other.m_) {
            std::stringstream ss;
            ss << "number of registers doesn't match: " << m_ << " != " << other.m_;
            throw std::invalid_argument(ss.str().c_str());
        }
        for (uint32_t r = 0; r < m_; ++r) {
            if (M_[r] < other.M_[r]) {
                M_[r] = other.M_[r];
            }
        }
    }

    /**
     * Clears all internal registers.
     */
    void clear() {
        std::fill(M_.begin(), M_.end(), 0);
    }

    /**
     * Returns size of register.
     *
     * @return Register size
     */
    uint32_t registerSize() const {
        return m_;
    }

    /**
     * Exchanges the content of the instance
     *
     * @param[in,out] rhs Another HyperLogLog instance
     */
    void swap(HyperLogLog& rhs) {
        std::swap(b_, rhs.b_);
        std::swap(m_, rhs.m_);
        std::swap(alphaMM_, rhs.alphaMM_);
        M_.swap(rhs.M_);
    }

    /**
     * Dump the current status to a stream
     *
     * @param[out] os The output stream where the data is saved
     *
     * @exception std::runtime_error When failed to dump.
     */
    void dump(std::ostream& os) const throw(std::runtime_error){
        os.write((char*)&b_, sizeof(b_));
        os.write((char*)&M_[0], sizeof(M_[0]) * M_.size());
        if(os.fail()){
            throw std::runtime_error("Failed to dump");
        }
    }

    /**
     * Restore the status from a stream
     *
     * @param[in] is The input stream where the status is saved
     *
     * @exception std::runtime_error When failed to restore.
     */
    void restore(std::istream& is) throw(std::runtime_error){
        uint8_t b = 0;
        is.read((char*)&b, sizeof(b));
        HyperLogLog tempHLL(b);
        is.read((char*)&(tempHLL.M_[0]), sizeof(M_[0]) * tempHLL.m_);
        if(is.fail()){
           throw std::runtime_error("Failed to restore");
        }
        swap(tempHLL);
    }

private:
    uint8_t b_; ///< register bit width
    uint32_t m_; ///< register size
    double alphaMM_; ///< alpha * m^2
    std::vector<uint8_t> M_; ///< registers

    uint8_t rho(uint32_t x, uint8_t b) {
        uint8_t v = 1;
        while (v <= b && !(x & 0x80000000)) {
            v++;
            x <<= 1;
        }
        return v;
    }

};

} // namespace hll

#endif // !defined(HYPERLOGLOG_HPP)
<commit_msg>hyperloglog: modify it according to our needs<commit_after>/*
 * Copyright (c) 2013 Hideaki Ohno <hide.o.j55{at}gmail.com>
 *
 * 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.
 */

/*
 * Copyright (C) 2011 Clearspring Technologies, Inc.
 *
 * 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.
 */

/*
 * Copyright 2015 Cloudius Systems
 *
 * Modified by Cloudius Systems
 */

#pragma once

/**
 * @file hyperloglog.hpp
 * @brief HyperLogLog cardinality estimator
 * @date Created 2013/3/20
 * @author Hideaki Ohno
 */

#include <vector>
#include <cmath>
#include <sstream>
#include <stdexcept>
#include <algorithm>
#if 0
#include "murmur3.h"
#endif

#define HLL_HASH_SEED 313

namespace hll {

static const double pow_2_32 = 4294967296.0; ///< 2^32
static const double neg_pow_2_32 = -4294967296.0; ///< -(2^32)

static inline size_t size_unsigned_var_int(unsigned int value) {
    size_t size = 0;
    while ((value & 0xFFFFFF80) != 0L) {
        size++;
        value >>= 7;
    }
    size++;
    return size;
}

static inline size_t write_unsigned_var_int(unsigned int value, uint8_t* to) {
    size_t size = 0;
    while ((value & 0xFFFFFF80) != 0L) {
        *to = (value & 0x7F) | 0x80;
        value >>= 7;
        to++;
        size++;
    }
    *to = value & 0x7F;
    size++;
    return size;
}

/** @class HyperLogLog
 *  @brief Implement of 'HyperLogLog' estimate cardinality algorithm
 */
class HyperLogLog {
public:

    /**
     * Constructor
     *
     * @param[in] b bit width (register size will be 2 to the b power).
     *            This value must be in the range[4,16].Default value is 4.
     *
     * @exception std::invalid_argument the argument is out of range.
     */
    HyperLogLog(uint8_t b = 4) throw (std::invalid_argument) :
            b_(b), m_(1 << b), M_(m_, 0) {

        if (b < 4 || 16 < b) {
            throw std::invalid_argument("bit width must be in the range [4,16]");
        }

        double alpha;
        switch (m_) {
            case 16:
                alpha = 0.673;
                break;
            case 32:
                alpha = 0.697;
                break;
            case 64:
                alpha = 0.709;
                break;
            default:
                alpha = 0.7213 / (1.0 + 1.079 / m_);
                break;
        }
        alphaMM_ = alpha * m_ * m_;
    }

    static HyperLogLog from_bytes(temporary_buffer<uint8_t> bytes) {
        // FIXME: implement class that creates a HyperLogLog from an array of bytes.
        // This will useful if we need to work with the cardinality data from the
        // compaction metadata.
        abort();
    }

    /**
     * Adds element to the estimator
     *
     * @param[in] str string to add
     * @param[in] len length of string
     */
#if 0
    void add(const char* str, uint32_t len) {
        uint32_t hash;
        MurmurHash3_x86_32(str, len, HLL_HASH_SEED, (void*) &hash);
        uint32_t index = hash >> (32 - b_);
        uint8_t rank = rho((hash << b_), 32 - b_);
        if (rank > M_[index]) {
            M_[index] = rank;
        }
    }
#endif
    void offer_hashed(uint64_t hash) {
        uint32_t index = hash >> (64 - b_);
        uint8_t rank = rho((hash << b_), 64 - b_);

        if (rank > M_[index]) {
            M_[index] = rank;
        }
    }

    /*
     * Calculate the size of buffer returned by get_bytes().
     */
    size_t get_bytes_size() {
        size_t size = 0;
        size += sizeof(int); // version
        size += size_unsigned_var_int(b_); // p; register width = b_.
        size += size_unsigned_var_int(0); // sp; // sparse set = 0.
        size += size_unsigned_var_int(0); // type;
        size += size_unsigned_var_int(M_.size()); // register size;
        size += M_.size();
        return size;
    }

    temporary_buffer<uint8_t> get_bytes() {
        // FIXME: add support to SPARSE format.
        static constexpr int VERSION = 2;

        size_t s = get_bytes_size();
        temporary_buffer<uint8_t> bytes(s);
        size_t offset = 0;
        // write version
        *unaligned_cast<int*>(bytes.get_write() + offset) = htonl(-VERSION);
        offset += sizeof(int);

        // write register width
        offset += write_unsigned_var_int(b_, bytes.get_write() + offset);
        // NOTE: write precision value for sparse set (not supported).
        offset += write_unsigned_var_int(0, bytes.get_write() + offset);
        // write type (NORMAL always!)
        offset += write_unsigned_var_int(0, bytes.get_write() + offset);
        // write register size
        offset += write_unsigned_var_int(M_.size(), bytes.get_write() + offset);
        // write register
        memcpy(bytes.get_write() + offset, M_.data(), M_.size());
        offset += M_.size();

        bytes.trim(offset);
        assert(s == offset);
        return bytes;
    }

    /**
     * Estimates cardinality value.
     *
     * @return Estimated cardinality value.
     */
    double estimate() const {
        double estimate;
        double sum = 0.0;
        for (uint32_t i = 0; i < m_; i++) {
            sum += 1.0 / pow(2.0, M_[i]);
        }
        estimate = alphaMM_ / sum; // E in the original paper
        if (estimate <= 2.5 * m_) {
            uint32_t zeros = 0;
            for (uint32_t i = 0; i < m_; i++) {
                if (M_[i] == 0) {
                    zeros++;
                }
            }
            if (zeros != 0) {
                estimate = m_ * log(static_cast<double>(m_)/ zeros);
            }
        } else if (estimate > (1.0 / 30.0) * pow_2_32) {
            estimate = neg_pow_2_32 * log(1.0 - (estimate / pow_2_32));
        }
        return estimate;
    }

    /**
     * Merges the estimate from 'other' into this object, returning the estimate of their union.
     * The number of registers in each must be the same.
     *
     * @param[in] other HyperLogLog instance to be merged
     *
     * @exception std::invalid_argument number of registers doesn't match.
     */
    void merge(const HyperLogLog& other) throw (std::invalid_argument) {
        if (m_ != other.m_) {
            std::stringstream ss;
            ss << "number of registers doesn't match: " << m_ << " != " << other.m_;
            throw std::invalid_argument(ss.str().c_str());
        }
        for (uint32_t r = 0; r < m_; ++r) {
            if (M_[r] < other.M_[r]) {
                M_[r] = other.M_[r];
            }
        }
    }

    /**
     * Clears all internal registers.
     */
    void clear() {
        std::fill(M_.begin(), M_.end(), 0);
    }

    /**
     * Returns size of register.
     *
     * @return Register size
     */
    uint32_t registerSize() const {
        return m_;
    }

    /**
     * Exchanges the content of the instance
     *
     * @param[in,out] rhs Another HyperLogLog instance
     */
    void swap(HyperLogLog& rhs) {
        std::swap(b_, rhs.b_);
        std::swap(m_, rhs.m_);
        std::swap(alphaMM_, rhs.alphaMM_);
        M_.swap(rhs.M_);
    }

    /**
     * Dump the current status to a stream
     *
     * @param[out] os The output stream where the data is saved
     *
     * @exception std::runtime_error When failed to dump.
     */
    void dump(std::ostream& os) const throw(std::runtime_error){
        os.write((char*)&b_, sizeof(b_));
        os.write((char*)&M_[0], sizeof(M_[0]) * M_.size());
        if(os.fail()){
            throw std::runtime_error("Failed to dump");
        }
    }

    /**
     * Restore the status from a stream
     *
     * @param[in] is The input stream where the status is saved
     *
     * @exception std::runtime_error When failed to restore.
     */
    void restore(std::istream& is) throw(std::runtime_error){
        uint8_t b = 0;
        is.read((char*)&b, sizeof(b));
        HyperLogLog tempHLL(b);
        is.read((char*)&(tempHLL.M_[0]), sizeof(M_[0]) * tempHLL.m_);
        if(is.fail()){
           throw std::runtime_error("Failed to restore");
        }
        swap(tempHLL);
    }

private:
    uint8_t b_; ///< register bit width
    uint32_t m_; ///< register size
    double alphaMM_; ///< alpha * m^2
    std::vector<uint8_t> M_; ///< registers

    uint8_t rho(uint32_t x, uint8_t b) {
        uint8_t v = 1;
        while (v <= b && !(x & 0x80000000)) {
            v++;
            x <<= 1;
        }
        return v;
    }

};

} // namespace hll
<|endoftext|>


<commit_before>#include "json_ui.hh"

#include "display_buffer.hh"
#include "keys.hh"
#include "file.hh"
#include "event_manager.hh"
#include "value.hh"
#include "unit_tests.hh"

#include <utility>

namespace Kakoune
{

template<typename T>
String to_json(ArrayView<const T> array)
{
    String res;
    for (auto& elem : array)
    {
        if (not res.empty())
            res += ", ";
        res += to_json(elem);
    }
    return "[" + res + "]";
}

template<typename T, MemoryDomain D>
String to_json(const Vector<T, D>& vec) { return to_json(ArrayView<const T>{vec}); }

String to_json(int i) { return to_string(i); }
String to_json(StringView s) { return format(R"("{}")", escape(s, "\"", '\\')); }

String to_json(Color color)
{
    if (color.color == Kakoune::Color::RGB)
    {
        char buffer[10];
        sprintf(buffer, R"("#%02x%02x%02x")", color.r, color.g, color.b);
        return buffer;
    }
    return to_json(color_to_str(color));
}

String to_json(Attribute attributes)
{
    struct { Attribute attr; StringView name; }
    attrs[] {
        { Attribute::Exclusive, "exclusive" },
        { Attribute::Underline, "underline" },
        { Attribute::Reverse, "reverse" },
        { Attribute::Blink, "blink" },
        { Attribute::Bold, "bold" },
        { Attribute::Dim, "dim" },
        { Attribute::Italic, "italic" },
    };

    String res;
    for (auto& attr : attrs)
    {
        if (not (attributes & attr.attr))
            continue;

        if (not res.empty())
            res += ", ";
        res += to_json(attr.name);
    }
    return "[" + res + "]";
}

String to_json(Face face)
{
    return format(R"(\{ "fg": {}, "bg": {}, "attributes": {} })",
                  to_json(face.fg), to_json(face.bg), to_json(face.attributes));
}

String to_json(const DisplayAtom& atom)
{
    return format(R"(\{ "face": {}, "contents": {} })", to_json(atom.face), to_json(atom.content()));
}

String to_json(const DisplayLine& line)
{
    return to_json(line.atoms());
}

String to_json(CharCoord coord)
{
    return format(R"(\{ "line": {}, "column": {} })", coord.line, coord.column);
}

String to_json(MenuStyle style)
{
    switch (style)
    {
        case MenuStyle::Prompt: return R"("prompt")";
        case MenuStyle::Inline: return R"("inline")";
    }
    return "";
}

String to_json(InfoStyle style)
{
    switch (style)
    {
        case InfoStyle::Prompt: return R"("prompt")";
        case InfoStyle::Inline: return R"("inline")";
        case InfoStyle::InlineAbove: return R"("inlineAbove")";
        case InfoStyle::InlineBelow: return R"("inlineBelow")";
        case InfoStyle::MenuDoc: return R"("menuDoc")";
    }
    return "";
}

String concat()
{
    return "";
}

template<typename First, typename... Args>
String concat(First&& first, Args&&... args)
{
    if (sizeof...(Args) != 0)
        return to_json(first) + ", " + concat(args...);
    return to_json(first);
}

template<typename... Args>
void rpc_call(StringView method, Args&&... args)
{
    auto q = format(R"(\{ "jsonrpc": "2.0", "method": "{}", "params": [{}] }{})",
                    method, concat(std::forward<Args>(args)...), "\n");

    write_stdout(q);
}

JsonUI::JsonUI()
    : m_stdin_watcher{0, [this](FDWatcher&, EventMode mode) {
        parse_requests(mode);
      }}, m_dimensions{24, 80}
{
    set_signal_handler(SIGINT, SIG_DFL);
}

void JsonUI::draw(const DisplayBuffer& display_buffer,
                  const Face& default_face)
{
    rpc_call("draw", display_buffer.lines(), default_face);
}

void JsonUI::draw_status(const DisplayLine& status_line,
                         const DisplayLine& mode_line,
                         const Face& default_face)
{
    rpc_call("draw_status", status_line, mode_line, default_face);
}

bool JsonUI::is_key_available()
{
    return not m_pending_keys.empty();
}

Key JsonUI::get_key()
{
    kak_assert(not m_pending_keys.empty());
    Key key = m_pending_keys.front();
    m_pending_keys.erase(m_pending_keys.begin());
    return key;
}

void JsonUI::menu_show(ConstArrayView<DisplayLine> items,
                       CharCoord anchor, Face fg, Face bg,
                       MenuStyle style)
{
    rpc_call("menu_show", items, anchor, fg, bg, style);
}

void JsonUI::menu_select(int selected)
{
    rpc_call("menu_show", selected);
}

void JsonUI::menu_hide()
{
    rpc_call("menu_hide");
}

void JsonUI::info_show(StringView title, StringView content,
                       CharCoord anchor, Face face,
                       InfoStyle style)
{
    rpc_call("info_show", title, content, anchor, face, style);
}

void JsonUI::info_hide()
{
    rpc_call("info_hide");
}

void JsonUI::refresh(bool force)
{
    rpc_call("refresh", force);
}

void JsonUI::set_input_callback(InputCallback callback)
{
    m_input_callback = std::move(callback);
}

void JsonUI::set_ui_options(const Options& options)
{
    // rpc_call("set_ui_options", options);
}

CharCoord JsonUI::dimensions()
{
    return m_dimensions;
}

using JsonArray = Vector<Value>;
using JsonObject = IdMap<Value>;

static bool is_digit(char c) { return c >= '0' and c <= '9'; }

std::tuple<Value, const char*>
parse_json(const char* pos, const char* end)
{
    using Result = std::tuple<Value, const char*>;

    if (not skip_while(pos, end, is_blank))
        return {};

    if (is_digit(*pos))
    {
        auto digit_end = pos;
        skip_while(digit_end, end, is_digit);
        return Result{ Value{str_to_int({pos, end})}, digit_end };
    }
    if (*pos == '"')
    {
        String value;
        bool escaped = false;
        ++pos;
        for (auto string_end = pos; string_end != end; ++string_end)
        {
            if (escaped)
            {
                escaped = false;
                value += StringView{pos, string_end};
                value.back() = *string_end;
                pos = string_end+1;
                continue;
            }
            if (*string_end == '\\')
                escaped = true;
            if (*string_end == '"')
            {
                value += StringView{pos, string_end};
                return Result{std::move(value), string_end+1};
            }
        }
        return {};
    }
    if (*pos == '[')
    {
        JsonArray array;
        ++pos;
        while (true)
        {
            Value element;
            std::tie(element, pos) = parse_json(pos, end);
            if (not element)
                return {};
            array.push_back(std::move(element));
            if (not skip_while(pos, end, is_blank))
                return {};

            if (*pos == ',')
                ++pos;
            else if (*pos == ']')
                return Result{std::move(array), pos+1};
            else
                throw runtime_error("unable to parse array, expected ',' or ']'");
        }
    }
    if (*pos == '{')
    {
        JsonObject object;
        ++pos;
        while (true)
        {
            Value name_value;
            std::tie(name_value, pos) = parse_json(pos, end);
            if (not name_value)
                return {};

            String& name = name_value.as<String>();
            if (not skip_while(pos, end, is_blank))
                return {};
            if (*pos++ != ':')
                throw runtime_error("expected :");

            Value element;
            std::tie(element, pos) = parse_json(pos, end);
            if (not element)
                return {};
            object.append({ std::move(name), std::move(element) });
            if (not skip_while(pos, end, is_blank))
                return {};

            if (*pos == ',')
                ++pos;
            else if (*pos == '}')
                return Result{std::move(object), pos+1};
            else
                throw runtime_error("unable to parse object, expected ',' or '}'");
        }
    }
    throw runtime_error("Could not parse json");
}

void JsonUI::eval_json(const Value& json)
{
    const JsonObject& object = json.as<JsonObject>();
    auto json_it = object.find("jsonrpc");
    if (json_it == object.end() or json_it->value.as<String>() != "2.0")
        throw runtime_error("invalid json rpc request");

    auto method_it = object.find("method");
    if (method_it == object.end())
        throw runtime_error("invalid json rpc request (method missing)");
    StringView method = method_it->value.as<String>();

    auto params_it = object.find("params");
    if (params_it == object.end())
        throw runtime_error("invalid json rpc request (params missing)");
    const JsonArray& params = params_it->value.as<JsonArray>();

    if (method == "keys")
    {
        for (auto& key_val : params)
        {
            for (auto& key : parse_keys(key_val.as<String>()))
                m_pending_keys.push_back(key);
        }
    }
    else if (method == "resize")
    {
        if (params.size() != 2)
            throw runtime_error("resize expects 2 parameters");

        CharCoord dim{params[0].as<int>(), params[1].as<int>()};
        m_dimensions = dim;
        m_pending_keys.push_back(resize(dim));
    }
    else
        throw runtime_error("unknown method");
}

static bool stdin_ready()
{
    fd_set  rfds;
    FD_ZERO(&rfds);
    FD_SET(0, &rfds);

    timeval tv;
    tv.tv_sec = 0;
    tv.tv_usec = 0;

    return select(1, &rfds, nullptr, nullptr, &tv) == 1;
}

void JsonUI::parse_requests(EventMode mode)
{
    constexpr size_t bufsize = 1024;
    char buf[bufsize];
    while (stdin_ready())
    {
        ssize_t size = read(0, buf, bufsize);
        if (size == -1 or size == 0)
            break;

        m_requests += StringView{buf, buf + size};
    }

    if (not m_requests.empty())
    {
        Value json;
        const char* pos;
        std::tie(json, pos) = parse_json(m_requests.begin(), m_requests.end());
        if (json)
        {
            try
            {
                eval_json(json);
            }
            catch (runtime_error& error)
            {
                write_stderr(format("error while executing request '{}': '{}'",
                                    StringView{m_requests.begin(), pos}, error.what()));
            }
            m_requests = String{pos, m_requests.end()};
        }
    }

    while (not m_pending_keys.empty())
        m_input_callback(mode);
}

UnitTest test_json_parser{[]()
{
    StringView json = R"({ "jsonrpc": "2.0", "method": "keys", "params": [ "b", "l", "a", "h" ] })";
    auto value = std::get<0>(parse_json(json.begin(), json.end()));
    kak_assert(value);
}};

}
<commit_msg>Fix json escaping of strings<commit_after>#include "json_ui.hh"

#include "display_buffer.hh"
#include "keys.hh"
#include "file.hh"
#include "event_manager.hh"
#include "value.hh"
#include "unit_tests.hh"

#include <utility>

namespace Kakoune
{

template<typename T>
String to_json(ArrayView<const T> array)
{
    String res;
    for (auto& elem : array)
    {
        if (not res.empty())
            res += ", ";
        res += to_json(elem);
    }
    return "[" + res + "]";
}

template<typename T, MemoryDomain D>
String to_json(const Vector<T, D>& vec) { return to_json(ArrayView<const T>{vec}); }

String to_json(int i) { return to_string(i); }
String to_json(StringView str)
{
    String res;
    res.reserve(str.length() + 4);
    res += '"';
    for (auto it = str.begin(), end = str.end(); it != end; )
    {
        auto next = std::find_if(it, end, [](char c) {
            return c == '\\' or c == '"' or (c >= 0 and c <= 0x1F);
        });

        res += StringView{it, next};
        if (next == end)
            break;

        char buf[7] = {'\\', *next, 0};
        if (*next >= 0 and *next <= 0x1F)
            sprintf(buf, "\\u%04x", *next);

        res += buf;
        it = next+1;
    }
    res += '"';
    return res;
}

String to_json(Color color)
{
    if (color.color == Kakoune::Color::RGB)
    {
        char buffer[10];
        sprintf(buffer, R"("#%02x%02x%02x")", color.r, color.g, color.b);
        return buffer;
    }
    return to_json(color_to_str(color));
}

String to_json(Attribute attributes)
{
    struct { Attribute attr; StringView name; }
    attrs[] {
        { Attribute::Exclusive, "exclusive" },
        { Attribute::Underline, "underline" },
        { Attribute::Reverse, "reverse" },
        { Attribute::Blink, "blink" },
        { Attribute::Bold, "bold" },
        { Attribute::Dim, "dim" },
        { Attribute::Italic, "italic" },
    };

    String res;
    for (auto& attr : attrs)
    {
        if (not (attributes & attr.attr))
            continue;

        if (not res.empty())
            res += ", ";
        res += to_json(attr.name);
    }
    return "[" + res + "]";
}

String to_json(Face face)
{
    return format(R"(\{ "fg": {}, "bg": {}, "attributes": {} })",
                  to_json(face.fg), to_json(face.bg), to_json(face.attributes));
}

String to_json(const DisplayAtom& atom)
{
    return format(R"(\{ "face": {}, "contents": {} })", to_json(atom.face), to_json(atom.content()));
}

String to_json(const DisplayLine& line)
{
    return to_json(line.atoms());
}

String to_json(CharCoord coord)
{
    return format(R"(\{ "line": {}, "column": {} })", coord.line, coord.column);
}

String to_json(MenuStyle style)
{
    switch (style)
    {
        case MenuStyle::Prompt: return R"("prompt")";
        case MenuStyle::Inline: return R"("inline")";
    }
    return "";
}

String to_json(InfoStyle style)
{
    switch (style)
    {
        case InfoStyle::Prompt: return R"("prompt")";
        case InfoStyle::Inline: return R"("inline")";
        case InfoStyle::InlineAbove: return R"("inlineAbove")";
        case InfoStyle::InlineBelow: return R"("inlineBelow")";
        case InfoStyle::MenuDoc: return R"("menuDoc")";
    }
    return "";
}

String concat()
{
    return "";
}

template<typename First, typename... Args>
String concat(First&& first, Args&&... args)
{
    if (sizeof...(Args) != 0)
        return to_json(first) + ", " + concat(args...);
    return to_json(first);
}

template<typename... Args>
void rpc_call(StringView method, Args&&... args)
{
    auto q = format(R"(\{ "jsonrpc": "2.0", "method": "{}", "params": [{}] }{})",
                    method, concat(std::forward<Args>(args)...), "\n");

    write_stdout(q);
}

JsonUI::JsonUI()
    : m_stdin_watcher{0, [this](FDWatcher&, EventMode mode) {
        parse_requests(mode);
      }}, m_dimensions{24, 80}
{
    set_signal_handler(SIGINT, SIG_DFL);
}

void JsonUI::draw(const DisplayBuffer& display_buffer,
                  const Face& default_face)
{
    rpc_call("draw", display_buffer.lines(), default_face);
}

void JsonUI::draw_status(const DisplayLine& status_line,
                         const DisplayLine& mode_line,
                         const Face& default_face)
{
    rpc_call("draw_status", status_line, mode_line, default_face);
}

bool JsonUI::is_key_available()
{
    return not m_pending_keys.empty();
}

Key JsonUI::get_key()
{
    kak_assert(not m_pending_keys.empty());
    Key key = m_pending_keys.front();
    m_pending_keys.erase(m_pending_keys.begin());
    return key;
}

void JsonUI::menu_show(ConstArrayView<DisplayLine> items,
                       CharCoord anchor, Face fg, Face bg,
                       MenuStyle style)
{
    rpc_call("menu_show", items, anchor, fg, bg, style);
}

void JsonUI::menu_select(int selected)
{
    rpc_call("menu_show", selected);
}

void JsonUI::menu_hide()
{
    rpc_call("menu_hide");
}

void JsonUI::info_show(StringView title, StringView content,
                       CharCoord anchor, Face face,
                       InfoStyle style)
{
    rpc_call("info_show", title, content, anchor, face, style);
}

void JsonUI::info_hide()
{
    rpc_call("info_hide");
}

void JsonUI::refresh(bool force)
{
    rpc_call("refresh", force);
}

void JsonUI::set_input_callback(InputCallback callback)
{
    m_input_callback = std::move(callback);
}

void JsonUI::set_ui_options(const Options& options)
{
    // rpc_call("set_ui_options", options);
}

CharCoord JsonUI::dimensions()
{
    return m_dimensions;
}

using JsonArray = Vector<Value>;
using JsonObject = IdMap<Value>;

static bool is_digit(char c) { return c >= '0' and c <= '9'; }

std::tuple<Value, const char*>
parse_json(const char* pos, const char* end)
{
    using Result = std::tuple<Value, const char*>;

    if (not skip_while(pos, end, is_blank))
        return {};

    if (is_digit(*pos))
    {
        auto digit_end = pos;
        skip_while(digit_end, end, is_digit);
        return Result{ Value{str_to_int({pos, end})}, digit_end };
    }
    if (*pos == '"')
    {
        String value;
        bool escaped = false;
        ++pos;
        for (auto string_end = pos; string_end != end; ++string_end)
        {
            if (escaped)
            {
                escaped = false;
                value += StringView{pos, string_end};
                value.back() = *string_end;
                pos = string_end+1;
                continue;
            }
            if (*string_end == '\\')
                escaped = true;
            if (*string_end == '"')
            {
                value += StringView{pos, string_end};
                return Result{std::move(value), string_end+1};
            }
        }
        return {};
    }
    if (*pos == '[')
    {
        JsonArray array;
        ++pos;
        while (true)
        {
            Value element;
            std::tie(element, pos) = parse_json(pos, end);
            if (not element)
                return {};
            array.push_back(std::move(element));
            if (not skip_while(pos, end, is_blank))
                return {};

            if (*pos == ',')
                ++pos;
            else if (*pos == ']')
                return Result{std::move(array), pos+1};
            else
                throw runtime_error("unable to parse array, expected ',' or ']'");
        }
    }
    if (*pos == '{')
    {
        JsonObject object;
        ++pos;
        while (true)
        {
            Value name_value;
            std::tie(name_value, pos) = parse_json(pos, end);
            if (not name_value)
                return {};

            String& name = name_value.as<String>();
            if (not skip_while(pos, end, is_blank))
                return {};
            if (*pos++ != ':')
                throw runtime_error("expected :");

            Value element;
            std::tie(element, pos) = parse_json(pos, end);
            if (not element)
                return {};
            object.append({ std::move(name), std::move(element) });
            if (not skip_while(pos, end, is_blank))
                return {};

            if (*pos == ',')
                ++pos;
            else if (*pos == '}')
                return Result{std::move(object), pos+1};
            else
                throw runtime_error("unable to parse object, expected ',' or '}'");
        }
    }
    throw runtime_error("Could not parse json");
}

void JsonUI::eval_json(const Value& json)
{
    const JsonObject& object = json.as<JsonObject>();
    auto json_it = object.find("jsonrpc");
    if (json_it == object.end() or json_it->value.as<String>() != "2.0")
        throw runtime_error("invalid json rpc request");

    auto method_it = object.find("method");
    if (method_it == object.end())
        throw runtime_error("invalid json rpc request (method missing)");
    StringView method = method_it->value.as<String>();

    auto params_it = object.find("params");
    if (params_it == object.end())
        throw runtime_error("invalid json rpc request (params missing)");
    const JsonArray& params = params_it->value.as<JsonArray>();

    if (method == "keys")
    {
        for (auto& key_val : params)
        {
            for (auto& key : parse_keys(key_val.as<String>()))
                m_pending_keys.push_back(key);
        }
    }
    else if (method == "resize")
    {
        if (params.size() != 2)
            throw runtime_error("resize expects 2 parameters");

        CharCoord dim{params[0].as<int>(), params[1].as<int>()};
        m_dimensions = dim;
        m_pending_keys.push_back(resize(dim));
    }
    else
        throw runtime_error("unknown method");
}

static bool stdin_ready()
{
    fd_set  rfds;
    FD_ZERO(&rfds);
    FD_SET(0, &rfds);

    timeval tv;
    tv.tv_sec = 0;
    tv.tv_usec = 0;

    return select(1, &rfds, nullptr, nullptr, &tv) == 1;
}

void JsonUI::parse_requests(EventMode mode)
{
    constexpr size_t bufsize = 1024;
    char buf[bufsize];
    while (stdin_ready())
    {
        ssize_t size = read(0, buf, bufsize);
        if (size == -1 or size == 0)
            break;

        m_requests += StringView{buf, buf + size};
    }

    if (not m_requests.empty())
    {
        Value json;
        const char* pos;
        std::tie(json, pos) = parse_json(m_requests.begin(), m_requests.end());
        if (json)
        {
            try
            {
                eval_json(json);
            }
            catch (runtime_error& error)
            {
                write_stderr(format("error while executing request '{}': '{}'",
                                    StringView{m_requests.begin(), pos}, error.what()));
            }
            m_requests = String{pos, m_requests.end()};
        }
    }

    while (not m_pending_keys.empty())
        m_input_callback(mode);
}

UnitTest test_json_parser{[]()
{
    StringView json = R"({ "jsonrpc": "2.0", "method": "keys", "params": [ "b", "l", "a", "h" ] })";
    auto value = std::get<0>(parse_json(json.begin(), json.end()));
    kak_assert(value);
}};

}
<|endoftext|>


<commit_before>// Copyright (c) 2006-2008 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

#include "chrome/browser/gtk/tabs/tab_gtk.h"

#include "app/gfx/path.h"
#include "app/l10n_util.h"
#include "app/resource_bundle.h"
#include "chrome/browser/gtk/gtk_dnd_util.h"
#include "chrome/browser/gtk/menu_gtk.h"
#include "grit/generated_resources.h"
#include "grit/theme_resources.h"

namespace {

void SetEmptyDragIcon(GtkWidget* widget) {
  GdkPixbuf* pixbuf = gdk_pixbuf_new(GDK_COLORSPACE_RGB, TRUE, 8, 1, 1);
  gtk_drag_source_set_icon_pixbuf(widget, pixbuf);
  g_object_unref(pixbuf);
}

}  // namespace

class TabGtk::ContextMenuController : public MenuGtk::Delegate {
 public:
  explicit ContextMenuController(TabGtk* tab)
      : tab_(tab) {
    menu_.reset(new MenuGtk(this, false));
    menu_->AppendMenuItemWithLabel(
        TabStripModel::CommandNewTab,
        l10n_util::GetStringUTF8(IDS_TAB_CXMENU_NEWTAB));
    menu_->AppendSeparator();
    menu_->AppendMenuItemWithLabel(
        TabStripModel::CommandReload,
        l10n_util::GetStringUTF8(IDS_TAB_CXMENU_RELOAD));
    menu_->AppendMenuItemWithLabel(
        TabStripModel::CommandDuplicate,
        l10n_util::GetStringUTF8(IDS_TAB_CXMENU_DUPLICATE));
    menu_->AppendSeparator();
    menu_->AppendMenuItemWithLabel(
        TabStripModel::CommandCloseTab,
        l10n_util::GetStringUTF8(IDS_TAB_CXMENU_CLOSETAB));
    menu_->AppendMenuItemWithLabel(
        TabStripModel::CommandCloseOtherTabs,
        l10n_util::GetStringUTF8(IDS_TAB_CXMENU_CLOSEOTHERTABS));
    menu_->AppendMenuItemWithLabel(
        TabStripModel::CommandCloseTabsToRight,
        l10n_util::GetStringUTF8(IDS_TAB_CXMENU_CLOSETABSTORIGHT));
    menu_->AppendMenuItemWithLabel(
        TabStripModel::CommandCloseTabsOpenedBy,
        l10n_util::GetStringUTF8(IDS_TAB_CXMENU_CLOSETABSOPENEDBY));
    menu_->AppendMenuItemWithLabel(
        TabStripModel::CommandRestoreTab,
        l10n_util::GetStringUTF8(IDS_RESTORE_TAB));
    menu_->AppendSeparator();
    menu_->AppendCheckMenuItemWithLabel(
        TabStripModel::CommandTogglePinned,
        l10n_util::GetStringUTF8(IDS_TAB_CXMENU_PIN_TAB));
  }

  virtual ~ContextMenuController() {}

  void RunMenu() {
    menu_->PopupAsContext(gtk_get_current_event_time());
  }

  void Cancel() {
    tab_ = NULL;
    menu_->Cancel();
  }

 private:
  // MenuGtk::Delegate implementation:
  virtual bool IsCommandEnabled(int command_id) const {
    if (!tab_)
      return false;

    return tab_->delegate()->IsCommandEnabledForTab(
        static_cast<TabStripModel::ContextMenuCommand>(command_id), tab_);
  }

  virtual bool IsItemChecked(int command_id) const {
    if (!tab_ || command_id != TabStripModel::CommandTogglePinned)
      return false;
    return tab_->is_pinned();
  }

  virtual void ExecuteCommand(int command_id) {
    if (!tab_)
      return;

    tab_->delegate()->ExecuteCommandForTab(
        static_cast<TabStripModel::ContextMenuCommand>(command_id), tab_);
  }

  // The context menu.
  scoped_ptr<MenuGtk> menu_;

  // The Tab the context menu was brought up for. Set to NULL when the menu
  // is canceled.
  TabGtk* tab_;

  DISALLOW_COPY_AND_ASSIGN(ContextMenuController);
};

///////////////////////////////////////////////////////////////////////////////
// TabGtk, public:

TabGtk::TabGtk(TabDelegate* delegate)
    : TabRendererGtk(delegate->GetThemeProvider()),
      delegate_(delegate),
      closing_(false),
      dragging_(false) {
  event_box_ = gtk_event_box_new();
  gtk_event_box_set_visible_window(GTK_EVENT_BOX(event_box_), FALSE);
  gtk_drag_source_set(event_box_, GDK_BUTTON1_MASK,
                      NULL, 0, GDK_ACTION_MOVE);
  GtkDndUtil::SetSourceTargetListFromCodeMask(event_box_,
                                              GtkDndUtil::CHROME_TAB);
  g_signal_connect(G_OBJECT(event_box_), "button-press-event",
                   G_CALLBACK(OnMousePress), this);
  g_signal_connect(G_OBJECT(event_box_), "button-release-event",
                   G_CALLBACK(OnMouseRelease), this);
  g_signal_connect(G_OBJECT(event_box_), "enter-notify-event",
                   G_CALLBACK(OnEnterNotify), this);
  g_signal_connect(G_OBJECT(event_box_), "leave-notify-event",
                   G_CALLBACK(OnLeaveNotify), this);
  g_signal_connect_after(G_OBJECT(event_box_), "drag-begin",
                           G_CALLBACK(OnDragBegin), this);
  g_signal_connect_after(G_OBJECT(event_box_), "drag-end",
                         G_CALLBACK(OnDragEnd), this);
  g_signal_connect_after(G_OBJECT(event_box_), "drag-failed",
                           G_CALLBACK(OnDragFailed), this);
  gtk_widget_add_events(event_box_,
        GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK |
        GDK_LEAVE_NOTIFY_MASK | GDK_LEAVE_NOTIFY_MASK);
  gtk_container_add(GTK_CONTAINER(event_box_), TabRendererGtk::widget());
  gtk_widget_show_all(event_box_);

  SetEmptyDragIcon(event_box_);
}

TabGtk::~TabGtk() {
  if (menu_controller_.get()) {
    // The menu is showing. Close the menu.
    menu_controller_->Cancel();

    // Invoke this so that we hide the highlight.
    ContextMenuClosed();
  }
}

// static
gboolean TabGtk::OnMousePress(GtkWidget* widget, GdkEventButton* event,
                              TabGtk* tab) {
  if (event->button == 1) {
    // Store whether or not we were selected just now... we only want to be
    // able to drag foreground tabs, so we don't start dragging the tab if
    // it was in the background.
    bool just_selected = !tab->IsSelected();
    if (just_selected) {
      tab->delegate_->SelectTab(tab);
    }
  } else if (event->button == 3) {
    tab->ShowContextMenu();
  }

  return TRUE;
}

// static
gboolean TabGtk::OnMouseRelease(GtkWidget* widget, GdkEventButton* event,
                                TabGtk* tab) {
  // Middle mouse up means close the tab, but only if the mouse is over it
  // (like a button).
  if (event->button == 2 &&
      event->x >= 0 && event->y >= 0 &&
      event->x < widget->allocation.width &&
      event->y < widget->allocation.height) {
    tab->delegate_->CloseTab(tab);
  }

  return TRUE;
}

// static
gboolean TabGtk::OnEnterNotify(GtkWidget* widget, GdkEventCrossing* event,
                               TabGtk* tab) {
  tab->OnMouseEntered();
  return TRUE;
}

// static
gboolean TabGtk::OnLeaveNotify(GtkWidget* widget, GdkEventCrossing* event,
                               TabGtk* tab) {
  tab->OnMouseExited();
  return TRUE;
}

// static
void TabGtk::OnDragBegin(GtkWidget* widget, GdkDragContext* context,
                         TabGtk* tab) {
  MessageLoopForUI::current()->AddObserver(tab);

  int x, y;
  gdk_window_get_pointer(tab->event_box_->window, &x, &y, NULL);

  // Make the mouse coordinate relative to the tab.
  x -= tab->bounds().x();
  y -= tab->bounds().y();

  tab->dragging_ = true;
  tab->delegate_->MaybeStartDrag(tab, gfx::Point(x, y));
}

// static
void TabGtk::OnDragEnd(GtkWidget* widget, GdkDragContext* context,
                       TabGtk* tab) {
  tab->dragging_ = false;
  // Notify the drag helper that we're done with any potential drag operations.
  // Clean up the drag helper, which is re-created on the next mouse press.
  tab->delegate_->EndDrag(false);

  MessageLoopForUI::current()->RemoveObserver(tab);
}

// static
gboolean TabGtk::OnDragFailed(GtkWidget* widget, GdkDragContext* context,
                              GtkDragResult result,
                              TabGtk* tab) {
  // TODO(jhawkins): Implement an EndDrag method that wraps up functionality
  // of OnDragEnd and OnDragFailed.  Take |result| into account for a canceled
  // drag action.
  OnDragEnd(widget, context, tab);
  return TRUE;
}

///////////////////////////////////////////////////////////////////////////////
// TabGtk, MessageLoop::Observer implementation:

void TabGtk::WillProcessEvent(GdkEvent* event) {
  // Nothing to do.
}

void TabGtk::DidProcessEvent(GdkEvent* event) {
  switch (event->type) {
    case GDK_MOTION_NOTIFY:
      delegate_->ContinueDrag(NULL);
      break;
    case GDK_GRAB_BROKEN:
      // If the user drags the mouse away from the dragged tab before the widget
      // is created, gtk loses the grab used for the drag and we're stuck in a
      // limbo where the drag is still active, but we don't get any
      // motion-notify-event signals.  Adding the grab back doesn't keep the
      // drag alive, but it does get us out of this bind by finishing the drag.
      if (delegate_->IsTabDetached(this))
        gtk_grab_add(widget());
      break;
    default:
      break;
  }
}

///////////////////////////////////////////////////////////////////////////////
// TabGtk, TabRendererGtk overrides:

bool TabGtk::IsSelected() const {
  return delegate_->IsTabSelected(this);
}

bool TabGtk::IsVisible() const {
  return GTK_WIDGET_FLAGS(event_box_) & GTK_VISIBLE;
}

void TabGtk::SetVisible(bool visible) const {
  if (visible) {
    gtk_widget_show(event_box_);
  } else {
    gtk_widget_hide(event_box_);
  }
}

void TabGtk::CloseButtonClicked() {
  delegate_->CloseTab(this);
}

void TabGtk::UpdateData(TabContents* contents, bool loading_only) {
  TabRendererGtk::UpdateData(contents, loading_only);
  std::wstring title = GetTitle();
  if (!title.empty()) {
    // Only show the tooltip if the title is truncated.
    gfx::Font font;
    if (font.GetStringWidth(title) > title_bounds().width()) {
      gtk_widget_set_tooltip_text(widget(), WideToUTF8(title).c_str());
    } else {
      gtk_widget_set_has_tooltip(widget(), FALSE);
    }
  }
}

///////////////////////////////////////////////////////////////////////////////
// TabGtk, private:

void TabGtk::ShowContextMenu() {
  if (!menu_controller_.get())
    menu_controller_.reset(new ContextMenuController(this));

  menu_controller_->RunMenu();
}

void TabGtk::ContextMenuClosed() {
  delegate()->StopAllHighlighting();
  menu_controller_.reset();
}
<commit_msg>gtk: Grab the pointer when we grab focus for the source tab's widget.  Fixes a crash where events were being sent to an unknown, unrealized widget.<commit_after>// Copyright (c) 2006-2008 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

#include "chrome/browser/gtk/tabs/tab_gtk.h"

#include "app/gfx/path.h"
#include "app/l10n_util.h"
#include "app/resource_bundle.h"
#include "chrome/browser/gtk/gtk_dnd_util.h"
#include "chrome/browser/gtk/menu_gtk.h"
#include "grit/generated_resources.h"
#include "grit/theme_resources.h"

namespace {

void SetEmptyDragIcon(GtkWidget* widget) {
  GdkPixbuf* pixbuf = gdk_pixbuf_new(GDK_COLORSPACE_RGB, TRUE, 8, 1, 1);
  gtk_drag_source_set_icon_pixbuf(widget, pixbuf);
  g_object_unref(pixbuf);
}

}  // namespace

class TabGtk::ContextMenuController : public MenuGtk::Delegate {
 public:
  explicit ContextMenuController(TabGtk* tab)
      : tab_(tab) {
    menu_.reset(new MenuGtk(this, false));
    menu_->AppendMenuItemWithLabel(
        TabStripModel::CommandNewTab,
        l10n_util::GetStringUTF8(IDS_TAB_CXMENU_NEWTAB));
    menu_->AppendSeparator();
    menu_->AppendMenuItemWithLabel(
        TabStripModel::CommandReload,
        l10n_util::GetStringUTF8(IDS_TAB_CXMENU_RELOAD));
    menu_->AppendMenuItemWithLabel(
        TabStripModel::CommandDuplicate,
        l10n_util::GetStringUTF8(IDS_TAB_CXMENU_DUPLICATE));
    menu_->AppendSeparator();
    menu_->AppendMenuItemWithLabel(
        TabStripModel::CommandCloseTab,
        l10n_util::GetStringUTF8(IDS_TAB_CXMENU_CLOSETAB));
    menu_->AppendMenuItemWithLabel(
        TabStripModel::CommandCloseOtherTabs,
        l10n_util::GetStringUTF8(IDS_TAB_CXMENU_CLOSEOTHERTABS));
    menu_->AppendMenuItemWithLabel(
        TabStripModel::CommandCloseTabsToRight,
        l10n_util::GetStringUTF8(IDS_TAB_CXMENU_CLOSETABSTORIGHT));
    menu_->AppendMenuItemWithLabel(
        TabStripModel::CommandCloseTabsOpenedBy,
        l10n_util::GetStringUTF8(IDS_TAB_CXMENU_CLOSETABSOPENEDBY));
    menu_->AppendMenuItemWithLabel(
        TabStripModel::CommandRestoreTab,
        l10n_util::GetStringUTF8(IDS_RESTORE_TAB));
    menu_->AppendSeparator();
    menu_->AppendCheckMenuItemWithLabel(
        TabStripModel::CommandTogglePinned,
        l10n_util::GetStringUTF8(IDS_TAB_CXMENU_PIN_TAB));
  }

  virtual ~ContextMenuController() {}

  void RunMenu() {
    menu_->PopupAsContext(gtk_get_current_event_time());
  }

  void Cancel() {
    tab_ = NULL;
    menu_->Cancel();
  }

 private:
  // MenuGtk::Delegate implementation:
  virtual bool IsCommandEnabled(int command_id) const {
    if (!tab_)
      return false;

    return tab_->delegate()->IsCommandEnabledForTab(
        static_cast<TabStripModel::ContextMenuCommand>(command_id), tab_);
  }

  virtual bool IsItemChecked(int command_id) const {
    if (!tab_ || command_id != TabStripModel::CommandTogglePinned)
      return false;
    return tab_->is_pinned();
  }

  virtual void ExecuteCommand(int command_id) {
    if (!tab_)
      return;

    tab_->delegate()->ExecuteCommandForTab(
        static_cast<TabStripModel::ContextMenuCommand>(command_id), tab_);
  }

  // The context menu.
  scoped_ptr<MenuGtk> menu_;

  // The Tab the context menu was brought up for. Set to NULL when the menu
  // is canceled.
  TabGtk* tab_;

  DISALLOW_COPY_AND_ASSIGN(ContextMenuController);
};

///////////////////////////////////////////////////////////////////////////////
// TabGtk, public:

TabGtk::TabGtk(TabDelegate* delegate)
    : TabRendererGtk(delegate->GetThemeProvider()),
      delegate_(delegate),
      closing_(false),
      dragging_(false) {
  event_box_ = gtk_event_box_new();
  gtk_event_box_set_visible_window(GTK_EVENT_BOX(event_box_), FALSE);
  gtk_drag_source_set(event_box_, GDK_BUTTON1_MASK,
                      NULL, 0, GDK_ACTION_MOVE);
  GtkDndUtil::SetSourceTargetListFromCodeMask(event_box_,
                                              GtkDndUtil::CHROME_TAB);
  g_signal_connect(G_OBJECT(event_box_), "button-press-event",
                   G_CALLBACK(OnMousePress), this);
  g_signal_connect(G_OBJECT(event_box_), "button-release-event",
                   G_CALLBACK(OnMouseRelease), this);
  g_signal_connect(G_OBJECT(event_box_), "enter-notify-event",
                   G_CALLBACK(OnEnterNotify), this);
  g_signal_connect(G_OBJECT(event_box_), "leave-notify-event",
                   G_CALLBACK(OnLeaveNotify), this);
  g_signal_connect_after(G_OBJECT(event_box_), "drag-begin",
                           G_CALLBACK(OnDragBegin), this);
  g_signal_connect_after(G_OBJECT(event_box_), "drag-end",
                         G_CALLBACK(OnDragEnd), this);
  g_signal_connect_after(G_OBJECT(event_box_), "drag-failed",
                           G_CALLBACK(OnDragFailed), this);
  gtk_widget_add_events(event_box_,
        GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK |
        GDK_LEAVE_NOTIFY_MASK | GDK_LEAVE_NOTIFY_MASK);
  gtk_container_add(GTK_CONTAINER(event_box_), TabRendererGtk::widget());
  gtk_widget_show_all(event_box_);

  SetEmptyDragIcon(event_box_);
}

TabGtk::~TabGtk() {
  if (menu_controller_.get()) {
    // The menu is showing. Close the menu.
    menu_controller_->Cancel();

    // Invoke this so that we hide the highlight.
    ContextMenuClosed();
  }
}

// static
gboolean TabGtk::OnMousePress(GtkWidget* widget, GdkEventButton* event,
                              TabGtk* tab) {
  if (event->button == 1) {
    // Store whether or not we were selected just now... we only want to be
    // able to drag foreground tabs, so we don't start dragging the tab if
    // it was in the background.
    bool just_selected = !tab->IsSelected();
    if (just_selected) {
      tab->delegate_->SelectTab(tab);
    }
  } else if (event->button == 3) {
    tab->ShowContextMenu();
  }

  return TRUE;
}

// static
gboolean TabGtk::OnMouseRelease(GtkWidget* widget, GdkEventButton* event,
                                TabGtk* tab) {
  // Middle mouse up means close the tab, but only if the mouse is over it
  // (like a button).
  if (event->button == 2 &&
      event->x >= 0 && event->y >= 0 &&
      event->x < widget->allocation.width &&
      event->y < widget->allocation.height) {
    tab->delegate_->CloseTab(tab);
  }

  return TRUE;
}

// static
gboolean TabGtk::OnEnterNotify(GtkWidget* widget, GdkEventCrossing* event,
                               TabGtk* tab) {
  tab->OnMouseEntered();
  return TRUE;
}

// static
gboolean TabGtk::OnLeaveNotify(GtkWidget* widget, GdkEventCrossing* event,
                               TabGtk* tab) {
  tab->OnMouseExited();
  return TRUE;
}

// static
void TabGtk::OnDragBegin(GtkWidget* widget, GdkDragContext* context,
                         TabGtk* tab) {
  MessageLoopForUI::current()->AddObserver(tab);

  int x, y;
  gdk_window_get_pointer(tab->event_box_->window, &x, &y, NULL);

  // Make the mouse coordinate relative to the tab.
  x -= tab->bounds().x();
  y -= tab->bounds().y();

  tab->dragging_ = true;
  tab->delegate_->MaybeStartDrag(tab, gfx::Point(x, y));
}

// static
void TabGtk::OnDragEnd(GtkWidget* widget, GdkDragContext* context,
                       TabGtk* tab) {
  // Release our grab on the pointer.
  gdk_pointer_ungrab(GDK_CURRENT_TIME);
  gtk_grab_remove(tab->widget());

  tab->dragging_ = false;
  // Notify the drag helper that we're done with any potential drag operations.
  // Clean up the drag helper, which is re-created on the next mouse press.
  tab->delegate_->EndDrag(false);

  MessageLoopForUI::current()->RemoveObserver(tab);
}

// static
gboolean TabGtk::OnDragFailed(GtkWidget* widget, GdkDragContext* context,
                              GtkDragResult result,
                              TabGtk* tab) {
  // TODO(jhawkins): Implement an EndDrag method that wraps up functionality
  // of OnDragEnd and OnDragFailed.  Take |result| into account for a canceled
  // drag action.
  OnDragEnd(widget, context, tab);
  return TRUE;
}

///////////////////////////////////////////////////////////////////////////////
// TabGtk, MessageLoop::Observer implementation:

void TabGtk::WillProcessEvent(GdkEvent* event) {
  // Nothing to do.
}

void TabGtk::DidProcessEvent(GdkEvent* event) {
  switch (event->type) {
    case GDK_MOTION_NOTIFY:
      delegate_->ContinueDrag(NULL);
      break;
    case GDK_GRAB_BROKEN:
      // If the user drags the mouse away from the dragged tab before the widget
      // is created, gtk loses the grab used for the drag and we're stuck in a
      // limbo where the drag is still active, but we don't get any
      // motion-notify-event signals.  Adding the grab back doesn't keep the
      // drag alive, but it does get us out of this bind by finishing the drag.
      if (delegate_->IsTabDetached(this)) {
        gdk_pointer_grab(widget()->window, FALSE, GDK_POINTER_MOTION_HINT_MASK,
                         NULL, NULL, GDK_CURRENT_TIME);
        gtk_grab_add(widget());
      }
      break;
    default:
      break;
  }
}

///////////////////////////////////////////////////////////////////////////////
// TabGtk, TabRendererGtk overrides:

bool TabGtk::IsSelected() const {
  return delegate_->IsTabSelected(this);
}

bool TabGtk::IsVisible() const {
  return GTK_WIDGET_FLAGS(event_box_) & GTK_VISIBLE;
}

void TabGtk::SetVisible(bool visible) const {
  if (visible) {
    gtk_widget_show(event_box_);
  } else {
    gtk_widget_hide(event_box_);
  }
}

void TabGtk::CloseButtonClicked() {
  delegate_->CloseTab(this);
}

void TabGtk::UpdateData(TabContents* contents, bool loading_only) {
  TabRendererGtk::UpdateData(contents, loading_only);
  std::wstring title = GetTitle();
  if (!title.empty()) {
    // Only show the tooltip if the title is truncated.
    gfx::Font font;
    if (font.GetStringWidth(title) > title_bounds().width()) {
      gtk_widget_set_tooltip_text(widget(), WideToUTF8(title).c_str());
    } else {
      gtk_widget_set_has_tooltip(widget(), FALSE);
    }
  }
}

///////////////////////////////////////////////////////////////////////////////
// TabGtk, private:

void TabGtk::ShowContextMenu() {
  if (!menu_controller_.get())
    menu_controller_.reset(new ContextMenuController(this));

  menu_controller_->RunMenu();
}

void TabGtk::ContextMenuClosed() {
  delegate()->StopAllHighlighting();
  menu_controller_.reset();
}
<|endoftext|>


<commit_before>// Copyright (c) 2009 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

#include "chrome/common/transport_dib.h"

#include <unistd.h>
#include <sys/stat.h>
#include "base/shared_memory.h"

TransportDIB::TransportDIB()
    : size_(0) {
}

TransportDIB::TransportDIB(TransportDIB::Handle dib)
    : shared_memory_(dib, false /* read write */),
      size_(0) {
}

TransportDIB::~TransportDIB() {
}

// static
TransportDIB* TransportDIB::Create(size_t size, uint32 sequence_num) {
  TransportDIB* dib = new TransportDIB;
  if (!dib->shared_memory_.Create(L"", false /* read write */,
                                  false /* do not open existing */, size)) {
    delete dib;
    return NULL;
  }

  dib->size_ = size;
  return dib;
}

// static
TransportDIB* TransportDIB::Map(TransportDIB::Handle handle) {
  TransportDIB* dib = new TransportDIB(handle);
  struct stat st;
  fstat(handle.fd, &st);

  if (!dib->shared_memory_.Map(st.st_size)) {
    delete dib;
    close(handle.fd);
    return false;
  }

  dib->size_ = st.st_size;

  return dib;
}

void* TransportDIB::memory() const {
  return shared_memory_.memory();
}

TransportDIB::Id TransportDIB::id() const {
  return false;
}

TransportDIB::Handle TransportDIB::handle() const {
  return shared_memory_.handle();
}
<commit_msg>Mac rendering fix<commit_after>// Copyright (c) 2009 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

#include "chrome/common/transport_dib.h"

#include <unistd.h>
#include <sys/stat.h>
#include "base/shared_memory.h"

TransportDIB::TransportDIB()
    : size_(0) {
}

TransportDIB::TransportDIB(TransportDIB::Handle dib)
    : shared_memory_(dib, false /* read write */),
      size_(0) {
}

TransportDIB::~TransportDIB() {
}

// static
TransportDIB* TransportDIB::Create(size_t size, uint32 sequence_num) {
  TransportDIB* dib = new TransportDIB;
  if (!dib->shared_memory_.Create(L"", false /* read write */,
                                  false /* do not open existing */, size)) {
    delete dib;
    return NULL;
  }

  dib->size_ = size;
  return dib;
}

// static
TransportDIB* TransportDIB::Map(TransportDIB::Handle handle) {
  TransportDIB* dib = new TransportDIB(handle);
  struct stat st;
  fstat(handle.fd, &st);

  if (!dib->shared_memory_.Map(st.st_size)) {
    delete dib;
    close(handle.fd);
    return false;
  }

  dib->size_ = st.st_size;

  return dib;
}

void* TransportDIB::memory() const {
  return shared_memory_.memory();
}

TransportDIB::Id TransportDIB::id() const {
  return shared_memory_.id();
}

TransportDIB::Handle TransportDIB::handle() const {
  return shared_memory_.handle();
}
<|endoftext|>


<commit_before>#include <stddef.h>
#include <stdint.h>

/* Check if the compiler thinks if we are targeting the wrong operating system. */
#if defined(__linux__)
#error "You are not using a cross-compiler, you will most certainly run into trouble"
#endif

/* For now, it only work for the 32-bit x86 targets. */
#if !defined(__i386__)
#error "This tutorial needs to be compiled with a ix86-elf compiler"
#endif

/* Hardware text mode color constants. */
enum vga_color
{
    COLOR_BLACK = 0,
    COLOR_BLUE = 1,
    COLOR_GREEN = 2,
    COLOR_CYAN = 3,
    COLOR_RED = 4,
    COLOR_MAGENTA = 5,
    COLOR_BROWN = 6,
    COLOR_LIGHT_GREY = 7,
    COLOR_DARK_GREY = 8,
    COLOR_LIGHT_BLUE = 9,
    COLOR_LIGHT_GREEN = 10,
    COLOR_LIGHT_CYAN = 11,
    COLOR_LIGHT_RED = 12,
    COLOR_LIGHT_MAGENTA = 13,
    COLOR_LIGHT_BROWN = 14,
    COLOR_WHITE = 15,
};

/* make_color
 * ----------
 *
 * Create a 8 bytes color for vga input.
 *
 * Merge foreground and background color in a single 8 bytes variables.
 *
 * bytes :  1 | 2 | 3 | 4 | 5 | 6 | 7 | 8
 * values: bg |bg |bg |bg |fg |fg |fg |fg
 */
uint8_t make_color(enum vga_color fg, enum vga_color bg)
{
    return fg | bg << 4;
}

/* make_vgaentry
 * -------------
 *
 * Create a 16 bytes word for vga test input.
 *
 * Merge color and text in a single 16 bytes variables.
 *
 * bytes : 1 ... 8 | 9 ... 16
 * values: colors  | texts
 *
 */
uint16_t make_vgaentry(char c, uint8_t color)
{
    uint16_t c16 = c;
    uint16_t color16 = color;
    return c16 | color16 << 8;
}

//Default size for VGA buffer
static const size_t VGA_WIDTH = 80;
static const size_t VGA_HEIGHT = 25;

// Current row location
size_t terminal_row;
// Current column location
size_t terminal_column;
// Current color
uint8_t terminal_color;
// VGA buffer to write text
uint16_t* terminal_buffer;

/*terminal_initialize
 *-------------------
 *
 * Initialize terminal setting cursor at upper right corner and setting
 * the default color. Fill the VGA buffer with space as it could be
 * unititialize.
 */
void terminal_initialize()
{
    terminal_row = 0;
    terminal_column = 0;
    terminal_color = make_color(COLOR_LIGHT_GREY, COLOR_BLACK);
    // hardware VGA buffer location
    terminal_buffer = (uint16_t*) 0xB8000;
    for(size_t y = 0; y < VGA_HEIGHT; y++)
    {
        for(size_t x = 0; x < VGA_WIDTH; x++)
        {
            const size_t index = y * VGA_WIDTH + x;
            terminal_buffer[index] = make_vgaentry(' ', terminal_color);
        }
    }
}

/*terminal_putentryat
 *-------------------
 *
 * Write a char in the terminal.
 */
void terminal_putentryat(char c, uint8_t color, size_t x, size_t y)
{
    const size_t index = y * VGA_WIDTH + x;
    terminal_buffer[index] = make_vgaentry(c, color);
}

/*terminal_writestring
 *--------------------
 *
 * Write a char in the terminal and move cursor accordingly
 */
void terminal_putchar(char c)
{
    terminal_putentryat(c, terminal_color, terminal_column, terminal_row);
    if(++terminal_column == VGA_WIDTH)
    {
        terminal_column = 0;
        if(++terminal_row == VGA_HEIGHT)
        {
            terminal_row = 0;
        }
    }
}

/*terminal_writestring
 *--------------------
 *
 * Write the given string in the terminal.
 */
void terminal_writestring(const char* data)
{
    size_t i = 0;
    while(data[i] != 0)
        terminal_putchar(data[i++]);
}

/*kernel_main
 *-----------
 *
 * Startup function for the kernel.
 */
extern "C"
void kernel_main()
{
    terminal_initialize();
    terminal_writestring("Hello, kernel World!");
}
<commit_msg>Add support for new line in terminal<commit_after>#include <stddef.h>
#include <stdint.h>

/* Check if the compiler thinks if we are targeting the wrong operating system. */
#if defined(__linux__)
#error "You are not using a cross-compiler, you will most certainly run into trouble"
#endif

/* For now, it only work for the 32-bit x86 targets. */
#if !defined(__i386__)
#error "This tutorial needs to be compiled with a ix86-elf compiler"
#endif

/* Hardware text mode color constants. */
enum vga_color
{
    COLOR_BLACK = 0,
    COLOR_BLUE = 1,
    COLOR_GREEN = 2,
    COLOR_CYAN = 3,
    COLOR_RED = 4,
    COLOR_MAGENTA = 5,
    COLOR_BROWN = 6,
    COLOR_LIGHT_GREY = 7,
    COLOR_DARK_GREY = 8,
    COLOR_LIGHT_BLUE = 9,
    COLOR_LIGHT_GREEN = 10,
    COLOR_LIGHT_CYAN = 11,
    COLOR_LIGHT_RED = 12,
    COLOR_LIGHT_MAGENTA = 13,
    COLOR_LIGHT_BROWN = 14,
    COLOR_WHITE = 15,
};

/* make_color
 * ----------
 *
 * Create a 8 bytes color for vga input.
 *
 * Merge foreground and background color in a single 8 bytes variables.
 *
 * bytes :  1 | 2 | 3 | 4 | 5 | 6 | 7 | 8
 * values: bg |bg |bg |bg |fg |fg |fg |fg
 */
uint8_t make_color(enum vga_color fg, enum vga_color bg)
{
    return fg | bg << 4;
}

/* make_vgaentry
 * -------------
 *
 * Create a 16 bytes word for vga test input.
 *
 * Merge color and text in a single 16 bytes variables.
 *
 * bytes : 1 ... 8 | 9 ... 16
 * values: colors  | texts
 *
 */
uint16_t make_vgaentry(char c, uint8_t color)
{
    uint16_t c16 = c;
    uint16_t color16 = color;
    return c16 | color16 << 8;
}

//Default size for VGA buffer
static const size_t VGA_WIDTH = 80;
static const size_t VGA_HEIGHT = 25;

// Current row location
size_t terminal_row;
// Current column location
size_t terminal_column;
// Current color
uint8_t terminal_color;
// VGA buffer to write text
uint16_t* terminal_buffer;

/*terminal_initialize
 *-------------------
 *
 * Initialize terminal setting cursor at upper right corner and setting
 * the default color. Fill the VGA buffer with space as it could be
 * unititialize.
 */
void terminal_initialize()
{
    terminal_row = 0;
    terminal_column = 0;
    terminal_color = make_color(COLOR_LIGHT_GREY, COLOR_BLACK);
    // hardware VGA buffer location
    terminal_buffer = (uint16_t*) 0xB8000;
    for(size_t y = 0; y < VGA_HEIGHT; y++)
    {
        for(size_t x = 0; x < VGA_WIDTH; x++)
        {
            const size_t index = y * VGA_WIDTH + x;
            terminal_buffer[index] = make_vgaentry(' ', terminal_color);
        }
    }
}

/*terminal_putentryat
 *-------------------
 *
 * Write a char in the terminal.
 */
void terminal_putentryat(char c, uint8_t color, size_t x, size_t y)
{
    const size_t index = y * VGA_WIDTH + x;
    terminal_buffer[index] = make_vgaentry(c, color);
}

/*terminal_putchar
 *----------------
 *
 * Write a char in the terminal and move cursor accordingly
 */
void terminal_putchar(char c)
{
    if(c == '\n')
    {
        terminal_column = 0;
        if(++terminal_row == VGA_HEIGHT)
        {
            terminal_row = 0;
        }
    } else {
        terminal_putentryat(c, terminal_color, terminal_column, terminal_row);
        if(++terminal_column == VGA_WIDTH)
        {
            terminal_column = 0;
            if(++terminal_row == VGA_HEIGHT)
            {
                terminal_row = 0;
            }
        }
    }
}

/*terminal_writestring
 *--------------------
 *
 * Write the given string in the terminal.
 */
void terminal_writestring(const char* data)
{
    size_t i = 0;
    while(data[i] != 0)
        terminal_putchar(data[i++]);
}

/*kernel_main
 *-----------
 *
 * Startup function for the kernel.
 */
extern "C"
void kernel_main()
{
    terminal_initialize();
    for(size_t i = 0; i < 25; i++)
    {
        terminal_writestring("Hello World!\n");
    }
    terminal_writestring("Hello, kernel World!");
}
<|endoftext|>