/* Wayland compositor running on top of an X server.

Copyright (C) 2022 to various contributors.

This file is part of 12to11.

12to11 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.

12to11 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 12to11.  If not, see <https://www.gnu.org/licenses/>.  */

#include <stdlib.h>
#include <stdio.h>
#include <string.h>

#include "compositor.h"

#define AtomTableSize	256

typedef struct _AtomTable AtomTable;

/* This is automatically generated by mime4.awk.  */
Atom DirectTransferAtoms;

/* Simple hash table for atoms.  */

struct _AtomTable
{
  /* Atom array indexed by table size.  */
  Atom *atoms[AtomTableSize];

  /* Atom name array indexed by table size.  */
  char **names[AtomTableSize];

  /* Size of each array.  */
  ptrdiff_t atoms_length[AtomTableSize];
};

/* Array of all atom names.  */
static const char *names[] =
  {
    "_NET_WM_OPAQUE_REGION",
    "_XL_BUFFER_RELEASE",
    "_NET_WM_SYNC_REQUEST_COUNTER",
    "_NET_WM_FRAME_DRAWN",
    "WM_DELETE_WINDOW",
    "WM_PROTOCOLS",
    "_NET_SUPPORTING_WM_CHECK",
    "_NET_SUPPORTED",
    "_NET_WM_SYNC_REQUEST",
    "_MOTIF_WM_HINTS",
    "_NET_WM_STATE_MAXIMIZED_VERT",
    "_NET_WM_STATE_MAXIMIZED_HORZ",
    "_NET_WM_STATE_FOCUSED",
    "_NET_WM_STATE_FULLSCREEN",
    "_NET_WM_STATE",
    "_NET_WM_MOVERESIZE",
    "_GTK_FRAME_EXTENTS",
    "WM_TRANSIENT_FOR",
    "_XL_DMA_BUF_CREATED",
    "_GTK_SHOW_WINDOW_MENU",
    "_NET_WM_ALLOWED_ACTIONS",
    "_NET_WM_ACTION_FULLSCREEN",
    "_NET_WM_ACTION_MAXIMIZE_HORZ",
    "_NET_WM_ACTION_MAXIMIZE_VERT",
    "_NET_WM_ACTION_MINIMIZE",
    "INCR",
    "CLIPBOARD",
    "TARGETS",
    "UTF8_STRING",
    "_XL_SERVER_TIME_ATOM",
    "MULTIPLE",
    "TIMESTAMP",
    "ATOM_PAIR",
    "_NET_WM_NAME",
    "WM_NAME",
    "MANAGER",
    "_XSETTINGS_SETTINGS",
    "libinput Scroll Methods Available",
    "XdndAware",
    "XdndSelection",
    "XdndTypeList",
    "XdndActionCopy",
    "XdndActionMove",
    "XdndActionLink",
    "XdndActionAsk",
    "XdndActionPrivate",
    "XdndActionList",
    "XdndActionDescription",
    "XdndProxy",
    "XdndEnter",
    "XdndPosition",
    "XdndStatus",
    "XdndLeave",
    "XdndDrop",
    "XdndFinished",
    "_NET_WM_FRAME_TIMINGS",
    "_NET_WM_BYPASS_COMPOSITOR",
    "WM_STATE",
    "_NET_WM_WINDOW_TYPE",
    "_NET_WM_WINDOW_TYPE_MENU",
    "_NET_WM_WINDOW_TYPE_DND",
    "CONNECTOR_ID",
    "_NET_WM_PID",
    "_NET_WM_PING",
    "libinput Scrolling Pixel Distance",

    /* These are automatically generated from mime.txt.  */
    DirectTransferAtomNames
  };

Atom _NET_WM_OPAQUE_REGION, _XL_BUFFER_RELEASE, _NET_WM_SYNC_REQUEST_COUNTER,
  _NET_WM_FRAME_DRAWN, WM_DELETE_WINDOW, WM_PROTOCOLS,
  _NET_SUPPORTING_WM_CHECK, _NET_SUPPORTED, _NET_WM_SYNC_REQUEST,
  _MOTIF_WM_HINTS, _NET_WM_STATE_MAXIMIZED_VERT, _NET_WM_STATE_MAXIMIZED_HORZ,
  _NET_WM_STATE_FOCUSED, _NET_WM_STATE_FULLSCREEN, _NET_WM_STATE,
  _NET_WM_MOVERESIZE, _GTK_FRAME_EXTENTS, WM_TRANSIENT_FOR,
  _XL_DMA_BUF_CREATED, _GTK_SHOW_WINDOW_MENU, _NET_WM_ALLOWED_ACTIONS,
  _NET_WM_ACTION_FULLSCREEN, _NET_WM_ACTION_MAXIMIZE_HORZ,
  _NET_WM_ACTION_MAXIMIZE_VERT, _NET_WM_ACTION_MINIMIZE, INCR, CLIPBOARD,
  TARGETS, UTF8_STRING, _XL_SERVER_TIME_ATOM, MULTIPLE, TIMESTAMP, ATOM_PAIR,
  _NET_WM_NAME, WM_NAME, MANAGER, _XSETTINGS_SETTINGS,
  libinput_Scroll_Methods_Available, XdndAware, XdndSelection, XdndTypeList,
  XdndActionCopy, XdndActionMove, XdndActionLink, XdndActionAsk,
  XdndActionPrivate, XdndActionList, XdndActionDescription, XdndProxy,
  XdndEnter, XdndPosition, XdndStatus, XdndLeave, XdndDrop, XdndFinished,
  _NET_WM_FRAME_TIMINGS, _NET_WM_BYPASS_COMPOSITOR, WM_STATE,
  _NET_WM_WINDOW_TYPE, _NET_WM_WINDOW_TYPE_MENU, _NET_WM_WINDOW_TYPE_DND,
  CONNECTOR_ID, _NET_WM_PID, _NET_WM_PING, libinput_Scrolling_Pixel_Distance;

XrmQuark resource_quark, app_quark, QString;

/* Hash table containing atoms.  */

static AtomTable atom_table;

static unsigned int
HashAtomString (const char *string)
{
  unsigned int i;

  i = 3323198485ul;
  for (; *string; ++string)
    {
      i ^= *string;
      i *= 0x5bd1e995;
      i ^= i >> 15;
    }
  return i;
}

Atom
InternAtom (const char *name)
{
  Atom atom;
  unsigned int hash;
  ptrdiff_t bucket_length, i;

  hash = HashAtomString (name) % AtomTableSize;
  bucket_length = atom_table.atoms_length[hash];

  for (i = 0; i < bucket_length; ++i)
    {
      if (!strcmp (atom_table.names[hash][i], name))
	return atom_table.atoms[hash][i];
    }

  atom = XInternAtom (compositor.display, name, False);

  atom_table.atoms_length[hash] = ++bucket_length;
  atom_table.names[hash]
    = XLRealloc (atom_table.names[hash],
		 bucket_length * sizeof *atom_table.names);
  atom_table.atoms[hash]
    = XLRealloc (atom_table.atoms[hash],
		 bucket_length * sizeof *atom_table.atoms);
  atom_table.names[hash][bucket_length - 1] = XLStrdup (name);
  atom_table.atoms[hash][bucket_length - 1] = atom;
  return atom;
}

void
ProvideAtom (const char *name, Atom atom)
{
  unsigned int hash;
  ptrdiff_t bucket_length, i;

  hash = HashAtomString (name) % AtomTableSize;
  bucket_length = atom_table.atoms_length[hash];

  for (i = 0; i < bucket_length; ++i)
    {
      if (!strcmp (atom_table.names[hash][i], name))
	/* The atom already exists; there is no need to update it.  */
	return;
    }

  atom_table.atoms_length[hash] = ++bucket_length;
  atom_table.names[hash]
    = XLRealloc (atom_table.names[hash],
		 bucket_length * sizeof *atom_table.names);
  atom_table.atoms[hash]
    = XLRealloc (atom_table.atoms[hash],
		 bucket_length * sizeof *atom_table.atoms);
  atom_table.names[hash][bucket_length - 1] = XLStrdup (name);
  atom_table.atoms[hash][bucket_length - 1] = atom;
}

void
XLInitAtoms (void)
{
  Atom atoms[ArrayElements (names)];

  if (!XInternAtoms (compositor.display, (char **) names,
		     ArrayElements (names), False,
		     atoms))
    {
      fprintf (stderr, "Failed to intern X atoms\n");
      exit (1);
    }

  _NET_WM_OPAQUE_REGION = atoms[0];
  _XL_BUFFER_RELEASE = atoms[1];
  _NET_WM_SYNC_REQUEST_COUNTER = atoms[2];
  _NET_WM_FRAME_DRAWN = atoms[3];
  WM_DELETE_WINDOW = atoms[4];
  WM_PROTOCOLS = atoms[5];
  _NET_SUPPORTING_WM_CHECK = atoms[6];
  _NET_SUPPORTED = atoms[7];
  _NET_WM_SYNC_REQUEST = atoms[8];
  _MOTIF_WM_HINTS = atoms[9];
  _NET_WM_STATE_MAXIMIZED_VERT = atoms[10];
  _NET_WM_STATE_MAXIMIZED_HORZ = atoms[11];
  _NET_WM_STATE_FOCUSED = atoms[12];
  _NET_WM_STATE_FULLSCREEN = atoms[13];
  _NET_WM_STATE = atoms[14];
  _NET_WM_MOVERESIZE = atoms[15];
  _GTK_FRAME_EXTENTS = atoms[16];
  WM_TRANSIENT_FOR = atoms[17];
  _XL_DMA_BUF_CREATED = atoms[18];
  _GTK_SHOW_WINDOW_MENU = atoms[19];
  _NET_WM_ALLOWED_ACTIONS = atoms[20];
  _NET_WM_ACTION_FULLSCREEN = atoms[21];
  _NET_WM_ACTION_MAXIMIZE_HORZ = atoms[22];
  _NET_WM_ACTION_MAXIMIZE_VERT = atoms[23];
  _NET_WM_ACTION_MINIMIZE = atoms[24];
  INCR = atoms[25];
  CLIPBOARD = atoms[26];
  TARGETS = atoms[27];
  UTF8_STRING = atoms[28];
  _XL_SERVER_TIME_ATOM = atoms[29];
  MULTIPLE = atoms[30];
  TIMESTAMP = atoms[31];
  ATOM_PAIR = atoms[32];
  _NET_WM_NAME = atoms[33];
  WM_NAME = atoms[34];
  MANAGER = atoms[35];
  _XSETTINGS_SETTINGS = atoms[36];
  libinput_Scroll_Methods_Available = atoms[37];
  XdndAware = atoms[38];
  XdndSelection = atoms[39];
  XdndTypeList = atoms[40];
  XdndActionCopy = atoms[41];
  XdndActionMove = atoms[42];
  XdndActionLink = atoms[43];
  XdndActionAsk = atoms[44];
  XdndActionPrivate = atoms[45];
  XdndActionList = atoms[46];
  XdndActionDescription = atoms[47];
  XdndProxy = atoms[48];
  XdndEnter = atoms[49];
  XdndPosition = atoms[50];
  XdndStatus = atoms[51];
  XdndLeave = atoms[52];
  XdndDrop = atoms[53];
  XdndFinished = atoms[54];
  _NET_WM_FRAME_TIMINGS = atoms[55];
  _NET_WM_BYPASS_COMPOSITOR = atoms[56];
  WM_STATE = atoms[57];
  _NET_WM_WINDOW_TYPE = atoms[58];
  _NET_WM_WINDOW_TYPE_MENU = atoms[59];
  _NET_WM_WINDOW_TYPE_DND = atoms[60];
  CONNECTOR_ID = atoms[61];
  _NET_WM_PID = atoms[62];
  _NET_WM_PING = atoms[63];
  libinput_Scrolling_Pixel_Distance = atoms[64];

  /* This is automatically generated.  */
  DirectTransferAtomInit (atoms, 65);

  /* Now, initialize quarks.  */
  resource_quark = XrmPermStringToQuark (compositor.resource_name);
  app_quark = XrmPermStringToQuark (compositor.app_name);
  QString = XrmPermStringToQuark ("String");
}
