/* Python interface to ui_file_style::color objects.
Copyright (C) 2008-2024 Free Software Foundation, Inc.
This file is part of GDB.
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 3 of the License, or
(at your option) any later version.
This program 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 General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see . */
#include "python-internal.h"
#include "py-color.h"
#include "cli/cli-decode.h"
/* Colorspace constants and their values. */
static struct {
const char *name;
color_space value;
} colorspace_constants[] =
{
{ "COLORSPACE_MONOCHROME", color_space::MONOCHROME },
{ "COLORSPACE_ANSI_8COLOR", color_space::ANSI_8COLOR },
{ "COLORSPACE_AIXTERM_16COLOR", color_space::AIXTERM_16COLOR },
{ "COLORSPACE_XTERM_256COLOR", color_space::XTERM_256COLOR },
{ "COLORSPACE_RGB_24BIT", color_space::RGB_24BIT },
};
/* A color. */
struct colorpy_object
{
PyObject_HEAD
/* Underlying value. */
ui_file_style::color color;
};
extern PyTypeObject colorpy_object_type;
/* See py-color.h. */
gdbpy_ref<>
create_color_object (const ui_file_style::color &color)
{
gdbpy_ref color_obj (PyObject_New (colorpy_object,
&colorpy_object_type));
if (color_obj == nullptr)
return nullptr;
color_obj->color = color;
return gdbpy_ref<> ((PyObject *) color_obj.release ());
}
/* See py-color.h. */
bool
gdbpy_is_color (PyObject *obj)
{
return PyObject_IsInstance (obj, (PyObject *) &colorpy_object_type);
}
/* See py-color.h. */
const ui_file_style::color &
gdbpy_get_color (PyObject *obj)
{
gdb_assert (gdbpy_is_color (obj));
colorpy_object *self = (colorpy_object *) obj;
return self->color;
}
/* Get an attribute. */
static PyObject *
get_attr (PyObject *obj, PyObject *attr_name)
{
if (! PyUnicode_Check (attr_name))
return PyObject_GenericGetAttr (obj, attr_name);
colorpy_object *self = (colorpy_object *) obj;
const ui_file_style::color &color = self->color;
if (! PyUnicode_CompareWithASCIIString (attr_name, "colorspace"))
{
int value = static_cast (color.colorspace ());
return gdb_py_object_from_longest (value).release ();
}
if (! PyUnicode_CompareWithASCIIString (attr_name, "is_none"))
return PyBool_FromLong (color.is_none ());
if (! PyUnicode_CompareWithASCIIString (attr_name, "is_indexed"))
return PyBool_FromLong (color.is_indexed ());
if (! PyUnicode_CompareWithASCIIString (attr_name, "is_direct"))
return PyBool_FromLong (color.is_direct ());
if (color.is_indexed ()
&& ! PyUnicode_CompareWithASCIIString (attr_name, "index"))
return gdb_py_object_from_longest (color.get_value ()).release ();
if (color.is_direct ()
&& ! PyUnicode_CompareWithASCIIString (attr_name, "components"))
{
uint8_t rgb[3];
color.get_rgb (rgb);
gdbpy_ref<> rgb_objects[3];
for (int i = 0; i < 3; ++i)
{
rgb_objects[i] = gdb_py_object_from_ulongest (rgb[i]);
if (rgb_objects[i] == nullptr)
return nullptr;
}
PyObject *comp = PyTuple_New (3);
if (comp == nullptr)
return nullptr;
for (int i = 0; i < 3; ++i)
PyTuple_SET_ITEM (comp, i, rgb_objects[i].release ());
return comp;
}
return PyObject_GenericGetAttr (obj, attr_name);
}
/* Implementation of Color.escape_sequence (self, is_fg) -> str. */
static PyObject *
colorpy_escape_sequence (PyObject *self, PyObject *is_fg_obj)
{
if (!gdbpy_is_color (self))
{
PyErr_SetString (PyExc_RuntimeError,
_("Object is not gdb.Color."));
return nullptr;
}
if (! PyBool_Check (is_fg_obj))
{
PyErr_SetString (PyExc_RuntimeError,
_("A boolean argument is required."));
return nullptr;
}
bool is_fg = is_fg_obj == Py_True;
std::string s = gdbpy_get_color (self).to_ansi (is_fg);
return host_string_to_python_string (s.c_str ()).release ();
}
/* Object initializer; fills color with value.
Use: __init__(VALUE = None, COLORSPACE = None)
VALUE is a string, integer, RGB-tuple or None.
COLORSPACE is the color space index.
Returns -1 on error, with a python exception set. */
static int
colorpy_init (PyObject *self, PyObject *args, PyObject *kwds)
{
colorpy_object *obj = (colorpy_object *) self;
PyObject *value_obj = nullptr;
PyObject *colorspace_obj = nullptr;
color_space colorspace = color_space::MONOCHROME;
if (! PyArg_ParseTuple (args, "|OO", &value_obj, &colorspace_obj))
return -1;
try
{
if (colorspace_obj)
{
if (PyLong_Check (colorspace_obj))
{
long colorspace_id = -1;
if (! gdb_py_int_as_long (colorspace_obj, &colorspace_id))
return -1;
if (!color_space_safe_cast (&colorspace, colorspace_id))
error (_("colorspace %ld is out of range."), colorspace_id);
}
else if (colorspace_obj == Py_None)
colorspace_obj = nullptr;
else
error (_("colorspace must be None or integer"));
}
if (value_obj == nullptr || value_obj == Py_None)
obj->color = ui_file_style::color (colorspace, -1);
else if (PyLong_Check (value_obj))
{
long value = -1;
if (! gdb_py_int_as_long (value_obj, &value))
return -1;
if (value < 0 || value > INT_MAX)
error (_("value %ld is out of range."), value);
if (colorspace_obj)
obj->color = ui_file_style::color (colorspace, value);
else
obj->color = ui_file_style::color (value);
}
else if (PyTuple_Check (value_obj))
{
if (colorspace_obj == nullptr || colorspace != color_space::RGB_24BIT)
error (_("colorspace must be gdb.COLORSPACE_RGB_24BIT with "
"value of tuple type."));
Py_ssize_t tuple_size = PyTuple_Size (value_obj);
if (tuple_size < 0)
return -1;
if (tuple_size != 3)
error (_("Tuple value with RGB must be of size 3."));
uint8_t rgb[3];
for (int i = 0; i < 3; ++i)
{
PyObject *item = PyTuple_GetItem (value_obj, i);
if (!PyLong_Check (item))
error (_("Item %d of an RGB tuple must be integer."), i);
long item_value = -1;
if (!gdb_py_int_as_long (item, &item_value))
return -1;
if (item_value < 0 || item_value > UINT8_MAX)
error (_("RGB item %ld is out of byte range."), item_value);
rgb[i] = static_cast (item_value);
}
obj->color = ui_file_style::color (rgb[0], rgb[1], rgb[2]);
}
else if (PyUnicode_Check (value_obj))
{
gdb::unique_xmalloc_ptr
str (python_string_to_host_string (value_obj));
if (str == nullptr)
return -1;
obj->color = parse_var_color (str.get());
if (colorspace_obj != nullptr
&& colorspace != obj->color.colorspace ())
error (_("colorspace doesn't match to the value."));
}
else
error (_("value must be one of None, integer, tuple or str."));
}
catch (const gdb_exception &except)
{
return gdbpy_handle_gdb_exception (-1, except);
}
Py_INCREF (self);
return 0;
}
static PyObject *
colorpy_str (PyObject *self)
{
colorpy_object *obj = reinterpret_cast (self);
return PyUnicode_FromString (obj->color.to_string ().c_str ());
}
/* Initialize the 'color' module. */
static int
gdbpy_initialize_color (void)
{
for (auto & pair : colorspace_constants)
if (PyModule_AddIntConstant (gdb_module, pair.name,
static_cast (pair.value)) < 0)
return -1;
colorpy_object_type.tp_new = PyType_GenericNew;
return gdbpy_type_ready (&colorpy_object_type, gdb_module);
}
/* Color methods. */
static PyMethodDef color_methods[] =
{
{ "escape_sequence", colorpy_escape_sequence, METH_O,
"escape_sequence (is_foreground) -> str.\n\
Return the ANSI escape sequence for this color.\n\
IS_FOREGROUND indicates whether this is a foreground or background color."},
{nullptr}
};
PyTypeObject colorpy_object_type =
{
PyVarObject_HEAD_INIT (nullptr, 0)
"gdb.Color", /*tp_name*/
sizeof (colorpy_object), /*tp_basicsize*/
0, /*tp_itemsize*/
0, /*tp_dealloc*/
0, /*tp_print*/
0, /*tp_getattr*/
0, /*tp_setattr*/
0, /*tp_compare*/
0, /*tp_repr*/
0, /*tp_as_number*/
0, /*tp_as_sequence*/
0, /*tp_as_mapping*/
0, /*tp_hash */
0, /*tp_call*/
colorpy_str, /*tp_str*/
get_attr, /*tp_getattro*/
0, /*tp_setattro*/
0, /*tp_as_buffer*/
Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE, /*tp_flags*/
"GDB color object", /* tp_doc */
0, /* tp_traverse */
0, /* tp_clear */
0, /* tp_richcompare */
0, /* tp_weaklistoffset */
0, /* tp_iter */
0, /* tp_iternext */
color_methods, /* tp_methods */
0, /* tp_members */
0, /* tp_getset */
0, /* tp_base */
0, /* tp_dict */
0, /* tp_descr_get */
0, /* tp_descr_set */
0, /* tp_dictoffset */
colorpy_init, /* tp_init */
0, /* tp_alloc */
};
GDBPY_INITIALIZE_FILE (gdbpy_initialize_color);